mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-08 01:28:26 -05:00
Merge from vscode 6e530127a1bb8ffbd1bfb77dc680c321dc0d71f5 (#6844)
This commit is contained in:
@@ -3998,8 +3998,12 @@ suite('autoClosingPairs', () => {
|
||||
{ open: '\'', close: '\'', notIn: ['string', 'comment'] },
|
||||
{ open: '\"', close: '\"', notIn: ['string'] },
|
||||
{ open: '`', close: '`', notIn: ['string', 'comment'] },
|
||||
{ open: '/**', close: ' */', notIn: ['string'] }
|
||||
{ open: '/**', close: ' */', notIn: ['string'] },
|
||||
{ open: 'begin', close: 'end', notIn: ['string'] }
|
||||
],
|
||||
__electricCharacterSupport: {
|
||||
docComment: { open: '/**', close: ' */' }
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -4439,6 +4443,28 @@ suite('autoClosingPairs', () => {
|
||||
mode.dispose();
|
||||
});
|
||||
|
||||
test('multi-character autoclose', () => {
|
||||
let mode = new AutoClosingMode();
|
||||
usingCursor({
|
||||
text: [
|
||||
'',
|
||||
],
|
||||
languageIdentifier: mode.getLanguageIdentifier()
|
||||
}, (model, cursor) => {
|
||||
|
||||
model.setValue('begi');
|
||||
cursor.setSelections('test', [new Selection(1, 5, 1, 5)]);
|
||||
cursorCommand(cursor, H.Type, { text: 'n' }, 'keyboard');
|
||||
assert.strictEqual(model.getLineContent(1), 'beginend');
|
||||
|
||||
model.setValue('/*');
|
||||
cursor.setSelections('test', [new Selection(1, 3, 1, 3)]);
|
||||
cursorCommand(cursor, H.Type, { text: '*' }, 'keyboard');
|
||||
assert.strictEqual(model.getLineContent(1), '/** */');
|
||||
});
|
||||
mode.dispose();
|
||||
});
|
||||
|
||||
test('issue #55314: Do not auto-close when ending with open', () => {
|
||||
const languageId = new LanguageIdentifier('myElectricMode', 5);
|
||||
class ElectricMode extends MockMode {
|
||||
@@ -4477,7 +4503,7 @@ suite('autoClosingPairs', () => {
|
||||
model.forceTokenization(model.getLineCount());
|
||||
assertType(model, cursor, 3, 4, '"', '"', `does not double quote when ending with open`);
|
||||
model.forceTokenization(model.getLineCount());
|
||||
assertType(model, cursor, 4, 2, '"', '""', `double quote when ending with open`);
|
||||
assertType(model, cursor, 4, 2, '"', '"', `does not double quote when ending with open`);
|
||||
model.forceTokenization(model.getLineCount());
|
||||
assertType(model, cursor, 4, 3, '"', '"', `does not double quote when ending with open`);
|
||||
});
|
||||
@@ -4772,31 +4798,18 @@ suite('autoClosingPairs', () => {
|
||||
|
||||
// on the mac US intl kb layout
|
||||
|
||||
// Typing ` + space
|
||||
// Typing ' + space
|
||||
cursorCommand(cursor, H.CompositionStart, null, 'keyboard');
|
||||
cursorCommand(cursor, H.Type, { text: '\'' }, 'keyboard');
|
||||
cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '\'' }, 'keyboard');
|
||||
cursorCommand(cursor, H.CompositionEnd, null, 'keyboard');
|
||||
|
||||
assert.equal(model.getValue(), '\'\'');
|
||||
|
||||
// Typing " + space within string
|
||||
cursor.setSelections('test', [new Selection(1, 2, 1, 2)]);
|
||||
cursorCommand(cursor, H.CompositionStart, null, 'keyboard');
|
||||
cursorCommand(cursor, H.Type, { text: '"' }, 'keyboard');
|
||||
cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '"' }, 'keyboard');
|
||||
cursorCommand(cursor, H.CompositionEnd, null, 'keyboard');
|
||||
|
||||
assert.equal(model.getValue(), '\'"\'');
|
||||
|
||||
// Typing ' + space after '
|
||||
model.setValue('\'');
|
||||
cursor.setSelections('test', [new Selection(1, 2, 1, 2)]);
|
||||
// Typing one more ' + space
|
||||
cursorCommand(cursor, H.CompositionStart, null, 'keyboard');
|
||||
cursorCommand(cursor, H.Type, { text: '\'' }, 'keyboard');
|
||||
cursorCommand(cursor, H.ReplacePreviousChar, { replaceCharCnt: 1, text: '\'' }, 'keyboard');
|
||||
cursorCommand(cursor, H.CompositionEnd, null, 'keyboard');
|
||||
|
||||
assert.equal(model.getValue(), '\'\'');
|
||||
|
||||
// Typing ' as a closing tag
|
||||
|
||||
@@ -7,6 +7,10 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { OpenerService } from 'vs/editor/browser/services/openerService';
|
||||
import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices';
|
||||
import { CommandsRegistry, ICommandService, NullCommandService } from 'vs/platform/commands/common/commands';
|
||||
import { deepClone } from 'vs/base/common/objects';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
suite('OpenerService', function () {
|
||||
|
||||
@@ -24,19 +28,77 @@ suite('OpenerService', function () {
|
||||
}
|
||||
};
|
||||
|
||||
function getStorageService(trustedDomainsSetting: string[]) {
|
||||
let _settings = deepClone(trustedDomainsSetting);
|
||||
|
||||
return new class implements IStorageService {
|
||||
get = () => JSON.stringify(_settings);
|
||||
store = (key: string, val: string) => _settings = JSON.parse(val);
|
||||
|
||||
// Don't care
|
||||
_serviceBrand: any;
|
||||
|
||||
onDidChangeStorage = () => ({ dispose: () => { } });
|
||||
onWillSaveState = () => ({ dispose: () => { } });
|
||||
|
||||
getBoolean = () => true;
|
||||
getNumber = () => 0;
|
||||
remove = () => { };
|
||||
logStorage = () => { };
|
||||
};
|
||||
}
|
||||
|
||||
function getDialogService() {
|
||||
return new class implements IDialogService {
|
||||
_showInvoked = 0;
|
||||
show = () => {
|
||||
this._showInvoked++;
|
||||
return Promise.resolve({} as any);
|
||||
}
|
||||
get confirmInvoked() { return this._showInvoked; }
|
||||
|
||||
// Don't care
|
||||
_serviceBrand: any;
|
||||
confirm = () => {
|
||||
return Promise.resolve({} as any);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getProductService(): IProductService {
|
||||
return new class {
|
||||
nameShort: 'VS Code';
|
||||
|
||||
_serviceBrand: any;
|
||||
} as IProductService;
|
||||
}
|
||||
|
||||
|
||||
setup(function () {
|
||||
lastCommand = undefined;
|
||||
});
|
||||
|
||||
test('delegate to editorService, scheme:///fff', function () {
|
||||
const openerService = new OpenerService(editorService, NullCommandService);
|
||||
const openerService = new OpenerService(
|
||||
editorService,
|
||||
NullCommandService,
|
||||
getStorageService([]),
|
||||
getDialogService(),
|
||||
getProductService()
|
||||
);
|
||||
openerService.open(URI.parse('another:///somepath'));
|
||||
assert.equal(editorService.lastInput!.options!.selection, undefined);
|
||||
});
|
||||
|
||||
test('delegate to editorService, scheme:///fff#L123', function () {
|
||||
|
||||
const openerService = new OpenerService(editorService, NullCommandService);
|
||||
const openerService = new OpenerService(
|
||||
editorService,
|
||||
NullCommandService,
|
||||
getStorageService([]),
|
||||
getDialogService(),
|
||||
getProductService()
|
||||
);
|
||||
|
||||
openerService.open(URI.parse('file:///somepath#L23'));
|
||||
assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23);
|
||||
@@ -59,7 +121,13 @@ suite('OpenerService', function () {
|
||||
|
||||
test('delegate to editorService, scheme:///fff#123,123', function () {
|
||||
|
||||
const openerService = new OpenerService(editorService, NullCommandService);
|
||||
const openerService = new OpenerService(
|
||||
editorService,
|
||||
NullCommandService,
|
||||
getStorageService([]),
|
||||
getDialogService(),
|
||||
getProductService()
|
||||
);
|
||||
|
||||
openerService.open(URI.parse('file:///somepath#23'));
|
||||
assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23);
|
||||
@@ -78,7 +146,13 @@ suite('OpenerService', function () {
|
||||
|
||||
test('delegate to commandsService, command:someid', function () {
|
||||
|
||||
const openerService = new OpenerService(editorService, commandService);
|
||||
const openerService = new OpenerService(
|
||||
editorService,
|
||||
commandService,
|
||||
getStorageService([]),
|
||||
getDialogService(),
|
||||
getProductService()
|
||||
);
|
||||
|
||||
const id = `aCommand${Math.random()}`;
|
||||
CommandsRegistry.registerCommand(id, function () { });
|
||||
@@ -98,4 +172,70 @@ suite('OpenerService', function () {
|
||||
assert.equal(lastCommand!.args[0], 12);
|
||||
assert.equal(lastCommand!.args[1], true);
|
||||
});
|
||||
|
||||
test('links are protected by dialog.show', function () {
|
||||
const dialogService = getDialogService();
|
||||
const openerService = new OpenerService(
|
||||
editorService,
|
||||
commandService,
|
||||
getStorageService([]),
|
||||
dialogService,
|
||||
getProductService()
|
||||
);
|
||||
|
||||
openerService.open(URI.parse('https://www.microsoft.com'));
|
||||
assert.equal(dialogService.confirmInvoked, 1);
|
||||
});
|
||||
|
||||
test('links on the whitelisted domains can be opened without dialog.show', function () {
|
||||
const dialogService = getDialogService();
|
||||
const openerService = new OpenerService(
|
||||
editorService,
|
||||
commandService,
|
||||
getStorageService(['https://microsoft.com']),
|
||||
dialogService,
|
||||
getProductService()
|
||||
);
|
||||
|
||||
openerService.open(URI.parse('https://microsoft.com'));
|
||||
openerService.open(URI.parse('https://microsoft.com/'));
|
||||
openerService.open(URI.parse('https://microsoft.com/en-us/'));
|
||||
openerService.open(URI.parse('https://microsoft.com/en-us/?foo=bar'));
|
||||
openerService.open(URI.parse('https://microsoft.com/en-us/?foo=bar#baz'));
|
||||
|
||||
assert.equal(dialogService.confirmInvoked, 0);
|
||||
});
|
||||
|
||||
test('variations of links are protected by dialog confirmation', function () {
|
||||
const dialogService = getDialogService();
|
||||
const openerService = new OpenerService(
|
||||
editorService,
|
||||
commandService,
|
||||
getStorageService(['https://microsoft.com']),
|
||||
dialogService,
|
||||
getProductService()
|
||||
);
|
||||
|
||||
openerService.open(URI.parse('http://microsoft.com'));
|
||||
openerService.open(URI.parse('https://www.microsoft.com'));
|
||||
|
||||
assert.equal(dialogService.confirmInvoked, 2);
|
||||
});
|
||||
|
||||
test('* removes all link protection', function () {
|
||||
const dialogService = getDialogService();
|
||||
const openerService = new OpenerService(
|
||||
editorService,
|
||||
commandService,
|
||||
getStorageService(['*']),
|
||||
dialogService,
|
||||
getProductService()
|
||||
);
|
||||
|
||||
openerService.open(URI.parse('https://code.visualstudio.com/'));
|
||||
openerService.open(URI.parse('https://www.microsoft.com'));
|
||||
openerService.open(URI.parse('https://www.github.com'));
|
||||
|
||||
assert.equal(dialogService.confirmInvoked, 0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ import * as assert from 'assert';
|
||||
import { StandardTokenType } from 'vs/editor/common/modes';
|
||||
import { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair';
|
||||
import { TokenText, createFakeScopedLineTokens } from 'vs/editor/test/common/modesTestUtils';
|
||||
import { StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration';
|
||||
|
||||
suite('CharacterPairSupport', () => {
|
||||
|
||||
@@ -52,8 +53,21 @@ suite('CharacterPairSupport', () => {
|
||||
assert.deepEqual(characaterPairSupport.getSurroundingPairs(), []);
|
||||
});
|
||||
|
||||
function findAutoClosingPair(characterPairSupport: CharacterPairSupport, character: string): StandardAutoClosingPairConditional | null {
|
||||
for (const autoClosingPair of characterPairSupport.getAutoClosingPairs()) {
|
||||
if (autoClosingPair.open === character) {
|
||||
return autoClosingPair;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function testShouldAutoClose(characterPairSupport: CharacterPairSupport, line: TokenText[], character: string, column: number): boolean {
|
||||
return characterPairSupport.shouldAutoClosePair(character, createFakeScopedLineTokens(line), column);
|
||||
const autoClosingPair = findAutoClosingPair(characterPairSupport, character);
|
||||
if (!autoClosingPair) {
|
||||
return false;
|
||||
}
|
||||
return CharacterPairSupport.shouldAutoClosePair(autoClosingPair, createFakeScopedLineTokens(line), column);
|
||||
}
|
||||
|
||||
test('shouldAutoClosePair in empty line', () => {
|
||||
|
||||
@@ -21,86 +21,20 @@ suite('Editor Modes - Auto Indentation', () => {
|
||||
assert.deepEqual(actual, null);
|
||||
}
|
||||
|
||||
function testAppends(electricCharacterSupport: BracketElectricCharacterSupport, line: TokenText[], character: string, offset: number, appendText: string): void {
|
||||
let actual = _testOnElectricCharacter(electricCharacterSupport, line, character, offset);
|
||||
assert.deepEqual(actual, { appendText: appendText });
|
||||
}
|
||||
|
||||
function testMatchBracket(electricCharacterSupport: BracketElectricCharacterSupport, line: TokenText[], character: string, offset: number, matchOpenBracket: string): void {
|
||||
let actual = _testOnElectricCharacter(electricCharacterSupport, line, character, offset);
|
||||
assert.deepEqual(actual, { matchOpenBracket: matchOpenBracket });
|
||||
}
|
||||
|
||||
test('Doc comments', () => {
|
||||
let brackets = new BracketElectricCharacterSupport(null, [{ open: '/**', close: ' */' }], null);
|
||||
|
||||
testAppends(brackets, [
|
||||
{ text: '/*', type: StandardTokenType.Other },
|
||||
], '*', 3, ' */');
|
||||
|
||||
testDoesNothing(brackets, [
|
||||
{ text: '/*', type: StandardTokenType.Other },
|
||||
{ text: ' ', type: StandardTokenType.Other },
|
||||
{ text: '*/', type: StandardTokenType.Other },
|
||||
], '*', 3);
|
||||
});
|
||||
|
||||
test('getElectricCharacters uses all sources and dedups', () => {
|
||||
let sup = new BracketElectricCharacterSupport(
|
||||
new RichEditBrackets(fakeLanguageIdentifier, [
|
||||
['{', '}'],
|
||||
['(', ')']
|
||||
]), [
|
||||
{ open: '{', close: '}', notIn: ['string', 'comment'] },
|
||||
{ open: '"', close: '"', notIn: ['string', 'comment'] },
|
||||
{ open: 'begin', close: 'end', notIn: ['string'] }
|
||||
],
|
||||
{ docComment: { open: '/**', close: ' */' } }
|
||||
])
|
||||
);
|
||||
|
||||
assert.deepEqual(sup.getElectricCharacters(), ['}', ')', 'n', '*']);
|
||||
});
|
||||
|
||||
test('auto-close', () => {
|
||||
let sup = new BracketElectricCharacterSupport(
|
||||
new RichEditBrackets(fakeLanguageIdentifier, [
|
||||
['{', '}'],
|
||||
['(', ')']
|
||||
]), [
|
||||
{ open: '{', close: '}', notIn: ['string', 'comment'] },
|
||||
{ open: '"', close: '"', notIn: ['string', 'comment'] },
|
||||
{ open: 'begin', close: 'end', notIn: ['string'] }
|
||||
],
|
||||
{ docComment: { open: '/**', close: ' */' } }
|
||||
);
|
||||
|
||||
testDoesNothing(sup, [], 'a', 0);
|
||||
|
||||
testDoesNothing(sup, [{ text: 'egi', type: StandardTokenType.Other }], 'b', 1);
|
||||
testDoesNothing(sup, [{ text: 'bgi', type: StandardTokenType.Other }], 'e', 2);
|
||||
testDoesNothing(sup, [{ text: 'bei', type: StandardTokenType.Other }], 'g', 3);
|
||||
testDoesNothing(sup, [{ text: 'beg', type: StandardTokenType.Other }], 'i', 4);
|
||||
|
||||
testDoesNothing(sup, [{ text: 'egin', type: StandardTokenType.Other }], 'b', 1);
|
||||
testDoesNothing(sup, [{ text: 'bgin', type: StandardTokenType.Other }], 'e', 2);
|
||||
testDoesNothing(sup, [{ text: 'bein', type: StandardTokenType.Other }], 'g', 3);
|
||||
testDoesNothing(sup, [{ text: 'begn', type: StandardTokenType.Other }], 'i', 4);
|
||||
testAppends(sup, [{ text: 'begi', type: StandardTokenType.Other }], 'n', 5, 'end');
|
||||
|
||||
testDoesNothing(sup, [{ text: '3gin', type: StandardTokenType.Other }], 'b', 1);
|
||||
testDoesNothing(sup, [{ text: 'bgin', type: StandardTokenType.Other }], '3', 2);
|
||||
testDoesNothing(sup, [{ text: 'b3in', type: StandardTokenType.Other }], 'g', 3);
|
||||
testDoesNothing(sup, [{ text: 'b3gn', type: StandardTokenType.Other }], 'i', 4);
|
||||
testDoesNothing(sup, [{ text: 'b3gi', type: StandardTokenType.Other }], 'n', 5);
|
||||
|
||||
testDoesNothing(sup, [{ text: 'begi', type: StandardTokenType.String }], 'n', 5);
|
||||
|
||||
testAppends(sup, [{ text: '"', type: StandardTokenType.String }, { text: 'begi', type: StandardTokenType.Other }], 'n', 6, 'end');
|
||||
testDoesNothing(sup, [{ text: '"', type: StandardTokenType.String }, { text: 'begi', type: StandardTokenType.String }], 'n', 6);
|
||||
|
||||
testAppends(sup, [{ text: '/*', type: StandardTokenType.String }], '*', 3, ' */');
|
||||
|
||||
testDoesNothing(sup, [{ text: 'begi', type: StandardTokenType.Other }, { text: 'end', type: StandardTokenType.Other }], 'n', 5);
|
||||
assert.deepEqual(sup.getElectricCharacters(), ['}', ')']);
|
||||
});
|
||||
|
||||
test('matchOpenBracket', () => {
|
||||
@@ -108,12 +42,7 @@ suite('Editor Modes - Auto Indentation', () => {
|
||||
new RichEditBrackets(fakeLanguageIdentifier, [
|
||||
['{', '}'],
|
||||
['(', ')']
|
||||
]), [
|
||||
{ open: '{', close: '}', notIn: ['string', 'comment'] },
|
||||
{ open: '"', close: '"', notIn: ['string', 'comment'] },
|
||||
{ open: 'begin', close: 'end', notIn: ['string'] }
|
||||
],
|
||||
{ docComment: { open: '/**', close: ' */' } }
|
||||
])
|
||||
);
|
||||
|
||||
testDoesNothing(sup, [{ text: '\t{', type: StandardTokenType.Other }], '\t', 1);
|
||||
|
||||
Reference in New Issue
Block a user