SQL Operations Studio Public Preview 1 (0.23) release source code

This commit is contained in:
Karl Burtram
2017-11-09 14:30:27 -08:00
parent b88ecb8d93
commit 3cdac41339
8829 changed files with 759707 additions and 286 deletions

View File

@@ -0,0 +1,89 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import {
getSelectionSearchString
} from 'vs/editor/contrib/find/common/find';
import { withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor';
suite('Find', () => {
test('search string at position', () => {
withMockCodeEditor([
'ABC DEF',
'0123 456'
], {}, (editor, cursor) => {
// The cursor is at the very top, of the file, at the first ABC
let searchStringAtTop = getSelectionSearchString(editor);
assert.equal(searchStringAtTop, 'ABC');
// Move cursor to the end of ABC
editor.setPosition(new Position(1, 3));
let searchStringAfterABC = getSelectionSearchString(editor);
assert.equal(searchStringAfterABC, 'ABC');
// Move cursor to DEF
editor.setPosition(new Position(1, 5));
let searchStringInsideDEF = getSelectionSearchString(editor);
assert.equal(searchStringInsideDEF, 'DEF');
});
});
test('search string with selection', () => {
withMockCodeEditor([
'ABC DEF',
'0123 456'
], {}, (editor, cursor) => {
// Select A of ABC
editor.setSelection(new Range(1, 1, 1, 2));
let searchStringSelectionA = getSelectionSearchString(editor);
assert.equal(searchStringSelectionA, 'A');
// Select BC of ABC
editor.setSelection(new Range(1, 2, 1, 4));
let searchStringSelectionBC = getSelectionSearchString(editor);
assert.equal(searchStringSelectionBC, 'BC');
// Select BC DE
editor.setSelection(new Range(1, 2, 1, 7));
let searchStringSelectionBCDE = getSelectionSearchString(editor);
assert.equal(searchStringSelectionBCDE, 'BC DE');
});
});
test('search string with multiline selection', () => {
withMockCodeEditor([
'ABC DEF',
'0123 456'
], {}, (editor, cursor) => {
// Select first line and newline
editor.setSelection(new Range(1, 1, 2, 1));
let searchStringSelectionWholeLine = getSelectionSearchString(editor);
assert.equal(searchStringSelectionWholeLine, null);
// Select first line and chunk of second
editor.setSelection(new Range(1, 1, 2, 4));
let searchStringSelectionTwoLines = getSelectionSearchString(editor);
assert.equal(searchStringSelectionTwoLines, null);
// Select end of first line newline and and chunk of second
editor.setSelection(new Range(1, 7, 2, 4));
let searchStringSelectionSpanLines = getSelectionSearchString(editor);
assert.equal(searchStringSelectionSpanLines, null);
});
});
});

View File

@@ -0,0 +1,871 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { TPromise } from 'vs/base/common/winjs.base';
import { Emitter } from 'vs/base/common/event';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
import { Range } from 'vs/editor/common/core/range';
import { EndOfLineSequence, ICommonCodeEditor, Handler } from 'vs/editor/common/editorCommon';
import {
CommonFindController, FindStartFocusAction, IFindStartOptions,
NextMatchFindAction, StartFindAction, SelectHighlightsAction,
AddSelectionToNextFindMatchAction
} from 'vs/editor/contrib/find/common/findController';
import { MockCodeEditor, withMockCodeEditor } from 'vs/editor/test/common/mocks/mockCodeEditor';
import { HistoryNavigator } from 'vs/base/common/history';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { Delayer } from 'vs/base/common/async';
class TestFindController extends CommonFindController {
public hasFocus: boolean;
public delayUpdateHistory: boolean = false;
public delayedUpdateHistoryPromise: TPromise<void>;
private _delayedUpdateHistoryEvent: Emitter<void> = new Emitter<void>();
constructor(editor: ICommonCodeEditor, @IContextKeyService contextKeyService: IContextKeyService, @IStorageService storageService: IStorageService) {
super(editor, contextKeyService, storageService);
this._updateHistoryDelayer = new Delayer<void>(50);
}
protected _start(opts: IFindStartOptions): void {
super._start(opts);
if (opts.shouldFocus !== FindStartFocusAction.NoFocusChange) {
this.hasFocus = true;
}
}
protected _delayedUpdateHistory() {
if (!this.delayedUpdateHistoryPromise) {
this.delayedUpdateHistoryPromise = new TPromise<void>((c, e) => {
const disposable = this._delayedUpdateHistoryEvent.event(() => {
disposable.dispose();
this.delayedUpdateHistoryPromise = null;
c(null);
});
});
}
if (this.delayUpdateHistory) {
super._delayedUpdateHistory();
} else {
this._updateHistory();
}
}
protected _updateHistory() {
super._updateHistory();
this._delayedUpdateHistoryEvent.fire();
}
}
function fromRange(rng: Range): number[] {
return [rng.startLineNumber, rng.startColumn, rng.endLineNumber, rng.endColumn];
}
suite('FindController', () => {
let queryState: { [key: string]: any; } = {};
let serviceCollection = new ServiceCollection();
serviceCollection.set(IStorageService, <any>{
get: (key: string) => queryState[key],
getBoolean: (key: string) => !!queryState[key],
store: (key: string, value: any) => { queryState[key] = value; }
});
test('issue #1857: F3, Find Next, acts like "Find Under Cursor"', () => {
withMockCodeEditor([
'ABC',
'ABC',
'XYZ',
'ABC'
], { serviceCollection: serviceCollection }, (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();
let nextMatchFindAction = new NextMatchFindAction();
// I hit Ctrl+F to show the Find dialog
startFindAction.run(null, editor);
// 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.pushUndoStop();
editor.executeEdits('test', [EditOperation.delete(new Range(1, 1, 1, 4))]);
editor.executeEdits('test', [EditOperation.insert(new Position(1, 1), 'XYZ')]);
editor.pushUndoStop();
// 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(null, editor);
assert.equal(findState.searchString, 'ABC');
assert.equal(findController.hasFocus, false);
findController.dispose();
});
});
test('issue #3090: F3 does not loop with two matches on a single line', () => {
withMockCodeEditor([
'import nls = require(\'vs/nls\');'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let nextMatchFindAction = new NextMatchFindAction();
editor.setPosition({
lineNumber: 1,
column: 9
});
nextMatchFindAction.run(null, editor);
assert.deepEqual(fromRange(editor.getSelection()), [1, 26, 1, 29]);
nextMatchFindAction.run(null, editor);
assert.deepEqual(fromRange(editor.getSelection()), [1, 8, 1, 11]);
findController.dispose();
});
});
test('issue #6149: Auto-escape highlighted text for search and replace regex mode', () => {
withMockCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let startFindAction = new StartFindAction();
let nextMatchFindAction = new NextMatchFindAction();
editor.setSelection(new Selection(1, 9, 1, 13));
findController.toggleRegex();
startFindAction.run(null, editor);
nextMatchFindAction.run(null, editor);
assert.deepEqual(fromRange(editor.getSelection()), [2, 9, 2, 13]);
nextMatchFindAction.run(null, editor);
assert.deepEqual(fromRange(editor.getSelection()), [1, 9, 1, 13]);
findController.dispose();
});
});
test('issue #8817: Cursor position changes when you cancel multicursor', () => {
withMockCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let selectHighlightsAction = new SelectHighlightsAction();
editor.setSelection(new Selection(2, 9, 2, 16));
selectHighlightsAction.run(null, editor);
assert.deepEqual(editor.getSelections().map(fromRange), [
[2, 9, 2, 16],
[1, 9, 1, 16],
[3, 9, 3, 16],
]);
editor.trigger('test', 'removeSecondaryCursors', null);
assert.deepEqual(fromRange(editor.getSelection()), [2, 9, 2, 16]);
findController.dispose();
});
});
test('issue #5400: "Select All Occurrences of Find Match" does not select all if find uses regex', () => {
withMockCodeEditor([
'something',
'someething',
'someeething',
'nothing'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let selectHighlightsAction = new SelectHighlightsAction();
editor.setSelection(new Selection(1, 1, 1, 1));
findController.getState().change({ searchString: 'some+thing', isRegex: true, isRevealed: true }, false);
selectHighlightsAction.run(null, editor);
assert.deepEqual(editor.getSelections().map(fromRange), [
[1, 1, 1, 10],
[2, 1, 2, 11],
[3, 1, 3, 12],
]);
assert.equal(findController.getState().searchString, 'some+thing');
findController.dispose();
});
});
test('issue #9043: Clear search scope when find widget is hidden', () => {
withMockCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.start({
forceRevealReplace: false,
seedSearchStringFromSelection: false,
shouldFocus: FindStartFocusAction.NoFocusChange,
shouldAnimate: false
});
assert.equal(findController.getState().searchScope, null);
findController.getState().change({
searchScope: new Range(1, 1, 1, 5)
}, false);
assert.deepEqual(findController.getState().searchScope, new Range(1, 1, 1, 5));
findController.closeFindWidget();
assert.equal(findController.getState().searchScope, null);
});
});
test('find term is added to history on state change', () => {
withMockCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.getState().change({ searchString: '1' }, false);
findController.getState().change({ searchString: '2' }, false);
findController.getState().change({ searchString: '3' }, false);
assert.deepEqual(['1', '2', '3'], toArray(findController.getHistory()));
});
});
test('find term is added with delay', (done) => {
withMockCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.delayUpdateHistory = true;
findController.getState().change({ searchString: '1' }, false);
findController.getState().change({ searchString: '2' }, false);
findController.getState().change({ searchString: '3' }, false);
findController.delayedUpdateHistoryPromise.then(() => {
assert.deepEqual(['3'], toArray(findController.getHistory()));
done();
});
});
});
test('show previous find term', () => {
withMockCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.getState().change({ searchString: '1' }, false);
findController.getState().change({ searchString: '2' }, false);
findController.getState().change({ searchString: '3' }, false);
findController.showPreviousFindTerm();
assert.deepEqual('2', findController.getState().searchString);
});
});
test('show previous find term do not update history', () => {
withMockCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.getState().change({ searchString: '1' }, false);
findController.getState().change({ searchString: '2' }, false);
findController.getState().change({ searchString: '3' }, false);
findController.showPreviousFindTerm();
assert.deepEqual(['1', '2', '3'], toArray(findController.getHistory()));
});
});
test('show next find term', () => {
withMockCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.getState().change({ searchString: '1' }, false);
findController.getState().change({ searchString: '2' }, false);
findController.getState().change({ searchString: '3' }, false);
findController.getState().change({ searchString: '4' }, false);
findController.showPreviousFindTerm();
findController.showPreviousFindTerm();
findController.showNextFindTerm();
assert.deepEqual('3', findController.getState().searchString);
});
});
test('show next find term do not update history', () => {
withMockCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.getState().change({ searchString: '1' }, false);
findController.getState().change({ searchString: '2' }, false);
findController.getState().change({ searchString: '3' }, false);
findController.getState().change({ searchString: '4' }, false);
findController.showPreviousFindTerm();
findController.showPreviousFindTerm();
findController.showNextFindTerm();
assert.deepEqual(['1', '2', '3', '4'], toArray(findController.getHistory()));
});
});
test('AddSelectionToNextFindMatchAction can work with multiline', () => {
withMockCodeEditor([
'',
'qwe',
'rty',
'',
'qwe',
'',
'rty',
'qwe',
'rty'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction();
editor.setSelection(new Selection(2, 1, 3, 4));
addSelectionToNextFindMatch.run(null, editor);
assert.deepEqual(editor.getSelections().map(fromRange), [
[2, 1, 3, 4],
[8, 1, 9, 4]
]);
editor.trigger('test', 'removeSecondaryCursors', null);
assert.deepEqual(fromRange(editor.getSelection()), [2, 1, 3, 4]);
findController.dispose();
});
});
test('issue #6661: AddSelectionToNextFindMatchAction can work with touching ranges', () => {
withMockCodeEditor([
'abcabc',
'abc',
'abcabc',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction();
editor.setSelection(new Selection(1, 1, 1, 4));
addSelectionToNextFindMatch.run(null, editor);
assert.deepEqual(editor.getSelections().map(fromRange), [
[1, 1, 1, 4],
[1, 4, 1, 7]
]);
addSelectionToNextFindMatch.run(null, editor);
addSelectionToNextFindMatch.run(null, editor);
addSelectionToNextFindMatch.run(null, editor);
assert.deepEqual(editor.getSelections().map(fromRange), [
[1, 1, 1, 4],
[1, 4, 1, 7],
[2, 1, 2, 4],
[3, 1, 3, 4],
[3, 4, 3, 7]
]);
editor.trigger('test', Handler.Type, { text: 'z' });
assert.deepEqual(editor.getSelections().map(fromRange), [
[1, 2, 1, 2],
[1, 3, 1, 3],
[2, 2, 2, 2],
[3, 2, 3, 2],
[3, 3, 3, 3]
]);
assert.equal(editor.getValue(), [
'zz',
'z',
'zz',
].join('\n'));
findController.dispose();
});
});
test('issue #23541: Multiline Ctrl+D does not work in CRLF files', () => {
withMockCodeEditor([
'',
'qwe',
'rty',
'',
'qwe',
'',
'rty',
'qwe',
'rty'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
editor.getModel().setEOL(EndOfLineSequence.CRLF);
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let addSelectionToNextFindMatch = new AddSelectionToNextFindMatchAction();
editor.setSelection(new Selection(2, 1, 3, 4));
addSelectionToNextFindMatch.run(null, editor);
assert.deepEqual(editor.getSelections().map(fromRange), [
[2, 1, 3, 4],
[8, 1, 9, 4]
]);
editor.trigger('test', 'removeSecondaryCursors', null);
assert.deepEqual(fromRange(editor.getSelection()), [2, 1, 3, 4]);
findController.dispose();
});
});
test('issue #18111: Regex replace with single space replaces with no space', () => {
withMockCodeEditor([
'HRESULT OnAmbientPropertyChange(DISPID dispid);'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let startFindAction = new StartFindAction();
startFindAction.run(null, editor);
findController.getState().change({ searchString: '\\b\\s{3}\\b', replaceString: ' ', isRegex: true }, false);
findController.moveToNextMatch();
assert.deepEqual(editor.getSelections().map(fromRange), [
[1, 39, 1, 42]
]);
findController.replace();
assert.deepEqual(editor.getValue(), 'HRESULT OnAmbientPropertyChange(DISPID dispid);');
findController.dispose();
});
});
test('issue #24714: Regular expression with ^ in search & replace', () => {
withMockCodeEditor([
'',
'line2',
'line3'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let startFindAction = new StartFindAction();
startFindAction.run(null, editor);
findController.getState().change({ searchString: '^', replaceString: 'x', isRegex: true }, false);
findController.moveToNextMatch();
assert.deepEqual(editor.getSelections().map(fromRange), [
[2, 1, 2, 1]
]);
findController.replace();
assert.deepEqual(editor.getValue(), '\nxline2\nline3');
findController.dispose();
});
});
function toArray(historyNavigator: HistoryNavigator<string>): string[] {
let result = [];
historyNavigator.first();
if (historyNavigator.current()) {
do {
result.push(historyNavigator.current());
} while (historyNavigator.next());
}
return result;
}
function testAddSelectionToNextFindMatchAction(text: string[], callback: (editor: MockCodeEditor, action: AddSelectionToNextFindMatchAction, findController: TestFindController) => void): void {
withMockCodeEditor(text, { serviceCollection: serviceCollection }, (editor, cursor) => {
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
let action = new AddSelectionToNextFindMatchAction();
callback(editor, action, findController);
findController.dispose();
});
}
test('AddSelectionToNextFindMatchAction starting with single collapsed selection', () => {
const text = [
'abc pizza',
'abc house',
'abc bar'
];
testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => {
editor.setSelections([
new Selection(1, 2, 1, 2),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
new Selection(3, 1, 3, 4),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
new Selection(3, 1, 3, 4),
]);
});
});
test('AddSelectionToNextFindMatchAction starting with two selections, one being collapsed 1)', () => {
const text = [
'abc pizza',
'abc house',
'abc bar'
];
testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => {
editor.setSelections([
new Selection(1, 1, 1, 4),
new Selection(2, 2, 2, 2),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
new Selection(3, 1, 3, 4),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
new Selection(3, 1, 3, 4),
]);
});
});
test('AddSelectionToNextFindMatchAction starting with two selections, one being collapsed 2)', () => {
const text = [
'abc pizza',
'abc house',
'abc bar'
];
testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => {
editor.setSelections([
new Selection(1, 2, 1, 2),
new Selection(2, 1, 2, 4),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
new Selection(3, 1, 3, 4),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
new Selection(3, 1, 3, 4),
]);
});
});
test('AddSelectionToNextFindMatchAction starting with all collapsed selections', () => {
const text = [
'abc pizza',
'abc house',
'abc bar'
];
testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => {
editor.setSelections([
new Selection(1, 2, 1, 2),
new Selection(2, 2, 2, 2),
new Selection(3, 1, 3, 1),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
new Selection(3, 1, 3, 4),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 4),
new Selection(2, 1, 2, 4),
new Selection(3, 1, 3, 4),
]);
});
});
test('AddSelectionToNextFindMatchAction starting with all collapsed selections on different words', () => {
const text = [
'abc pizza',
'abc house',
'abc bar'
];
testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => {
editor.setSelections([
new Selection(1, 6, 1, 6),
new Selection(2, 6, 2, 6),
new Selection(3, 6, 3, 6),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 5, 1, 10),
new Selection(2, 5, 2, 10),
new Selection(3, 5, 3, 8),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 5, 1, 10),
new Selection(2, 5, 2, 10),
new Selection(3, 5, 3, 8),
]);
});
});
test('issue #20651: AddSelectionToNextFindMatchAction case insensitive', () => {
const text = [
'test',
'testte',
'Test',
'testte',
'test'
];
testAddSelectionToNextFindMatchAction(text, (editor, action, findController) => {
editor.setSelections([
new Selection(1, 1, 1, 5),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 5),
new Selection(2, 1, 2, 5),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 5),
new Selection(2, 1, 2, 5),
new Selection(3, 1, 3, 5),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 5),
new Selection(2, 1, 2, 5),
new Selection(3, 1, 3, 5),
new Selection(4, 1, 4, 5),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 5),
new Selection(2, 1, 2, 5),
new Selection(3, 1, 3, 5),
new Selection(4, 1, 4, 5),
new Selection(5, 1, 5, 5),
]);
action.run(null, editor);
assert.deepEqual(editor.getSelections(), [
new Selection(1, 1, 1, 5),
new Selection(2, 1, 2, 5),
new Selection(3, 1, 3, 5),
new Selection(4, 1, 4, 5),
new Selection(5, 1, 5, 5),
]);
});
});
});
suite('FindController query options persistence', () => {
let queryState: { [key: string]: any; } = {};
queryState['editor.isRegex'] = false;
queryState['editor.matchCase'] = false;
queryState['editor.wholeWord'] = false;
let serviceCollection = new ServiceCollection();
serviceCollection.set(IStorageService, <any>{
get: (key: string) => queryState[key],
getBoolean: (key: string) => !!queryState[key],
store: (key: string, value: any) => { queryState[key] = value; }
});
test('matchCase', () => {
withMockCodeEditor([
'abc',
'ABC',
'XYZ',
'ABC'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
queryState = { 'editor.isRegex': false, 'editor.matchCase': true, 'editor.wholeWord': false };
// 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();
// I hit Ctrl+F to show the Find dialog
startFindAction.run(null, editor);
// I type ABC.
findState.change({ searchString: 'ABC' }, true);
// The second ABC is highlighted as matchCase is true.
assert.deepEqual(fromRange(editor.getSelection()), [2, 1, 2, 4]);
findController.dispose();
});
});
queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true };
test('wholeWord', () => {
withMockCodeEditor([
'ABC',
'AB',
'XYZ',
'ABC'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true };
// 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();
// I hit Ctrl+F to show the Find dialog
startFindAction.run(null, editor);
// I type AB.
findState.change({ searchString: 'AB' }, true);
// The second AB is highlighted as wholeWord is true.
assert.deepEqual(fromRange(editor.getSelection()), [2, 1, 2, 3]);
findController.dispose();
});
});
test('toggling options is saved', () => {
withMockCodeEditor([
'ABC',
'AB',
'XYZ',
'ABC'
], { serviceCollection: serviceCollection }, (editor, cursor) => {
queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true };
// The cursor is at the very top, of the file, at the first ABC
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.toggleRegex();
assert.equal(queryState['editor.isRegex'], true);
findController.dispose();
});
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,157 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { parseReplaceString, ReplacePattern, ReplacePiece } from 'vs/editor/contrib/find/common/replacePattern';
suite('Replace Pattern test', () => {
test('parse replace string', () => {
let testParse = (input: string, expectedPieces: ReplacePiece[]) => {
let actual = parseReplaceString(input);
let expected = new ReplacePattern(expectedPieces);
assert.deepEqual(actual, expected, 'Parsing ' + input);
};
// no backslash => no treatment
testParse('hello', [ReplacePiece.staticValue('hello')]);
// \t => TAB
testParse('\\thello', [ReplacePiece.staticValue('\thello')]);
testParse('h\\tello', [ReplacePiece.staticValue('h\tello')]);
testParse('hello\\t', [ReplacePiece.staticValue('hello\t')]);
// \n => LF
testParse('\\nhello', [ReplacePiece.staticValue('\nhello')]);
// \\t => \t
testParse('\\\\thello', [ReplacePiece.staticValue('\\thello')]);
testParse('h\\\\tello', [ReplacePiece.staticValue('h\\tello')]);
testParse('hello\\\\t', [ReplacePiece.staticValue('hello\\t')]);
// \\\t => \TAB
testParse('\\\\\\thello', [ReplacePiece.staticValue('\\\thello')]);
// \\\\t => \\t
testParse('\\\\\\\\thello', [ReplacePiece.staticValue('\\\\thello')]);
// \ at the end => no treatment
testParse('hello\\', [ReplacePiece.staticValue('hello\\')]);
// \ with unknown char => no treatment
testParse('hello\\x', [ReplacePiece.staticValue('hello\\x')]);
// \ with back reference => no treatment
testParse('hello\\0', [ReplacePiece.staticValue('hello\\0')]);
testParse('hello$&', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(0)]);
testParse('hello$0', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(0)]);
testParse('hello$02', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(0), ReplacePiece.staticValue('2')]);
testParse('hello$1', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(1)]);
testParse('hello$2', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(2)]);
testParse('hello$9', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(9)]);
testParse('$9hello', [ReplacePiece.matchIndex(9), ReplacePiece.staticValue('hello')]);
testParse('hello$12', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(12)]);
testParse('hello$99', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(99)]);
testParse('hello$99a', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(99), ReplacePiece.staticValue('a')]);
testParse('hello$1a', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(1), ReplacePiece.staticValue('a')]);
testParse('hello$100', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(10), ReplacePiece.staticValue('0')]);
testParse('hello$100a', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(10), ReplacePiece.staticValue('0a')]);
testParse('hello$10a0', [ReplacePiece.staticValue('hello'), ReplacePiece.matchIndex(10), ReplacePiece.staticValue('a0')]);
testParse('hello$$', [ReplacePiece.staticValue('hello$')]);
testParse('hello$$0', [ReplacePiece.staticValue('hello$0')]);
testParse('hello$`', [ReplacePiece.staticValue('hello$`')]);
testParse('hello$\'', [ReplacePiece.staticValue('hello$\'')]);
});
test('replace has JavaScript semantics', () => {
let testJSReplaceSemantics = (target: string, search: RegExp, replaceString: string, expected: string) => {
let replacePattern = parseReplaceString(replaceString);
let m = search.exec(target);
let actual = replacePattern.buildReplaceString(m);
assert.deepEqual(actual, expected, `${target}.replace(${search}, ${replaceString})`);
};
testJSReplaceSemantics('hi', /hi/, 'hello', 'hi'.replace(/hi/, 'hello'));
testJSReplaceSemantics('hi', /hi/, '\\t', 'hi'.replace(/hi/, '\t'));
testJSReplaceSemantics('hi', /hi/, '\\n', 'hi'.replace(/hi/, '\n'));
testJSReplaceSemantics('hi', /hi/, '\\\\t', 'hi'.replace(/hi/, '\\t'));
testJSReplaceSemantics('hi', /hi/, '\\\\n', 'hi'.replace(/hi/, '\\n'));
// implicit capture group 0
testJSReplaceSemantics('hi', /hi/, 'hello$&', 'hi'.replace(/hi/, 'hello$&'));
testJSReplaceSemantics('hi', /hi/, 'hello$0', 'hi'.replace(/hi/, 'hello$&'));
testJSReplaceSemantics('hi', /hi/, 'hello$&1', 'hi'.replace(/hi/, 'hello$&1'));
testJSReplaceSemantics('hi', /hi/, 'hello$01', 'hi'.replace(/hi/, 'hello$&1'));
// capture groups have funny semantics in replace strings
// the replace string interprets $nn as a captured group only if it exists in the search regex
testJSReplaceSemantics('hi', /(hi)/, 'hello$10', 'hi'.replace(/(hi)/, 'hello$10'));
testJSReplaceSemantics('hi', /(hi)()()()()()()()()()/, 'hello$10', 'hi'.replace(/(hi)()()()()()()()()()/, 'hello$10'));
testJSReplaceSemantics('hi', /(hi)/, 'hello$100', 'hi'.replace(/(hi)/, 'hello$100'));
testJSReplaceSemantics('hi', /(hi)/, 'hello$20', 'hi'.replace(/(hi)/, 'hello$20'));
});
test('get replace string if given text is a complete match', () => {
function assertReplace(target: string, search: RegExp, replaceString: string, expected: string): void {
let replacePattern = parseReplaceString(replaceString);
let m = search.exec(target);
let actual = replacePattern.buildReplaceString(m);
assert.equal(actual, expected, `${target}.replace(${search}, ${replaceString}) === ${expected}`);
}
assertReplace('bla', /bla/, 'hello', 'hello');
assertReplace('bla', /(bla)/, 'hello', 'hello');
assertReplace('bla', /(bla)/, 'hello$0', 'hellobla');
let searchRegex = /let\s+(\w+)\s*=\s*require\s*\(\s*['"]([\w\.\-/]+)\s*['"]\s*\)\s*/;
assertReplace('let fs = require(\'fs\')', searchRegex, 'import * as $1 from \'$2\';', 'import * as fs from \'fs\';');
assertReplace('let something = require(\'fs\')', searchRegex, 'import * as $1 from \'$2\';', 'import * as something from \'fs\';');
assertReplace('let something = require(\'fs\')', searchRegex, 'import * as $1 from \'$1\';', 'import * as something from \'something\';');
assertReplace('let something = require(\'fs\')', searchRegex, 'import * as $2 from \'$1\';', 'import * as fs from \'something\';');
assertReplace('let something = require(\'fs\')', searchRegex, 'import * as $0 from \'$0\';', 'import * as let something = require(\'fs\') from \'let something = require(\'fs\')\';');
assertReplace('let fs = require(\'fs\')', searchRegex, 'import * as $1 from \'$2\';', 'import * as fs from \'fs\';');
assertReplace('for ()', /for(.*)/, 'cat$1', 'cat ()');
// issue #18111
assertReplace('HRESULT OnAmbientPropertyChange(DISPID dispid);', /\b\s{3}\b/, ' ', ' ');
});
test('get replace string if match is sub-string of the text', () => {
function assertReplace(target: string, search: RegExp, replaceString: string, expected: string): void {
let replacePattern = parseReplaceString(replaceString);
let m = search.exec(target);
let actual = replacePattern.buildReplaceString(m);
assert.equal(actual, expected, `${target}.replace(${search}, ${replaceString}) === ${expected}`);
}
assertReplace('this is a bla text', /bla/, 'hello', 'hello');
assertReplace('this is a bla text', /this(?=.*bla)/, 'that', 'that');
assertReplace('this is a bla text', /(th)is(?=.*bla)/, '$1at', 'that');
assertReplace('this is a bla text', /(th)is(?=.*bla)/, '$1e', 'the');
assertReplace('this is a bla text', /(th)is(?=.*bla)/, '$1ere', 'there');
assertReplace('this is a bla text', /(th)is(?=.*bla)/, '$1', 'th');
assertReplace('this is a bla text', /(th)is(?=.*bla)/, 'ma$1', 'math');
assertReplace('this is a bla text', /(th)is(?=.*bla)/, 'ma$1s', 'maths');
assertReplace('this is a bla text', /(th)is(?=.*bla)/, '$0', 'this');
assertReplace('this is a bla text', /(th)is(?=.*bla)/, '$0$1', 'thisth');
assertReplace('this is a bla text', /bla(?=\stext$)/, 'foo', 'foo');
assertReplace('this is a bla text', /b(la)(?=\stext$)/, 'f$1', 'fla');
assertReplace('this is a bla text', /b(la)(?=\stext$)/, 'f$0', 'fbla');
assertReplace('this is a bla text', /b(la)(?=\stext$)/, '$0ah', 'blaah');
});
test('issue #19740 Find and replace capture group/backreference inserts `undefined` instead of empty string', () => {
let replacePattern = parseReplaceString('a{$1}');
let matches = /a(z)?/.exec('abcd');
let actual = replacePattern.buildReplaceString(matches);
assert.equal(actual, 'a{}');
});
});