diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 244740739b4fe639258143d03c9f5215664ef61d..aadb957cbc48e3af998ea693c407f876161635dc 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -1600,7 +1600,7 @@ export class EditorOptionsValidator { const _autoClosingOptions = (config: any, defaultValue: EditorAutoClosingOptions): EditorAutoClosingOptions => { if (typeof config === 'boolean') { return { autoClose: config, autoWrap: config, enabledBefore: ' \t\n' }; } - + if (!config) { config = {}; } let copy: EditorAutoClosingOptions = { autoClose: defaultValue.autoClose, autoWrap: defaultValue.autoWrap, enabledBefore: defaultValue.enabledBefore }; if (typeof config.autoClose === 'boolean') { copy.autoClose = config.autoClose; } if (typeof config.autoWrap === 'boolean') { copy.autoWrap = config.autoWrap; } diff --git a/src/vs/editor/common/controller/cursorDeleteOperations.ts b/src/vs/editor/common/controller/cursorDeleteOperations.ts index c214269277c0480387df08e1ba77adedd0d6ce09..bdbc5db16c2c2424f6847e80c5d5f10a00675500 100644 --- a/src/vs/editor/common/controller/cursorDeleteOperations.ts +++ b/src/vs/editor/common/controller/cursorDeleteOperations.ts @@ -65,7 +65,7 @@ export class DeleteOperations { const lineText = model.getLineContent(position.lineNumber); const character = lineText[position.column - 2]; - const characterIsQuote = (character === '\'' || character === '"'); + const characterIsQuote = (character === '\'' || character === '"' || character === '`'); const autoCloseConfig = characterIsQuote ? config.autoClosingQuotes : config.autoClosingBrackets; if (!config.autoClosingPairsOpen.hasOwnProperty(character) || !(autoCloseConfig.autoWrap || autoCloseConfig.autoClose)) { diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index f026298bc3139dcdf8acdfaa04efae9413a1ea1f..2316812ca775cf29f0e25ef65b0fd07f2a0c1ebc 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -437,7 +437,7 @@ export class TypeOperations { } private static _isAutoClosingCloseCharType(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string): boolean { - const chIsQuote = (ch === '\'' || ch === '"'); + const chIsQuote = (ch === '\'' || ch === '"' || ch === '`'); const autoCloseConfig = chIsQuote ? config.autoClosingQuotes : config.autoClosingBrackets; if (!autoCloseConfig.autoClose || !config.autoClosingPairsClose.hasOwnProperty(ch)) { @@ -514,7 +514,7 @@ export class TypeOperations { } private static _isAutoClosingOpenCharType(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string): boolean { - const chIsQuote = (ch === '\'' || ch === '"'); + const chIsQuote = (ch === '\'' || ch === '"' || ch === '`'); const autoCloseConfig = chIsQuote ? config.autoClosingQuotes : config.autoClosingBrackets; if (!autoCloseConfig.autoClose || !config.autoClosingPairsOpen.hasOwnProperty(ch)) { return false; @@ -587,7 +587,7 @@ export class TypeOperations { } private static _isSurroundSelectionType(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string): boolean { - const chIsQuote = (ch === '\'' || ch === '"'); + const chIsQuote = (ch === '\'' || ch === '"' || ch === '`'); const autoCloseConfig = chIsQuote ? config.autoClosingQuotes : config.autoClosingBrackets; if (!autoCloseConfig.autoWrap || !config.surroundingPairs.hasOwnProperty(ch)) { return false; @@ -620,7 +620,7 @@ export class TypeOperations { if (chIsQuote && selection.startLineNumber === selection.endLineNumber && selection.startColumn + 1 === selection.endColumn) { const selectionText = model.getValueInRange(selection); - if ((selectionText === '\'' || selectionText === '"')) { + if ((selectionText === '\'' || selectionText === '"' || selectionText === '`')) { // Typing a quote character on top of another quote character // => disable surround selection type return false; diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index 7c4d3136f273e79ff1eee88a882fcc42c522ae1a..9888d8afe6199b89b13cd969b94215839d637682 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -3999,14 +3999,14 @@ suite('autoClosingPairs', () => { }, (model, cursor) => { let autoClosePositions = [ - 'var| a| =| [|];|', - 'var| b| =| `asd`;|', - 'var| c| =| \'asd\';|', - 'var| d| =| "asd";|', - 'var| e| =| /*3*/| 3;|', - 'var| f| =| /**| 3| */3;|', - 'var| g| =| (3+5|);|', - 'var| h| =| {| a:| \'value\'| |};|', + 'var| a| =| [|]|;|', + 'var| b| =| `asd`|;|', + 'var| c| =| \'asd\'|;|', + 'var| d| =| "asd"|;|', + 'var| e| =| /*3*/| 3|;|', + 'var| f| =| /**| 3| */3|;|', + 'var| g| =| (3+5|)|;|', + 'var| h| =| {| a|:| \'value\'| |}|;|', ]; for (let i = 0, len = autoClosePositions.length; i < len; i++) { const lineNumber = i + 1; @@ -4025,6 +4025,159 @@ suite('autoClosingPairs', () => { mode.dispose(); }); + + test('configurable open parens', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + 'var a = [];', + 'var b = `asd`;', + 'var c = \'asd\';', + 'var d = "asd";', + 'var e = /*3*/ 3;', + 'var f = /** 3 */3;', + 'var g = (3+5);', + 'var h = { a: \'value\' };', + ], + languageIdentifier: mode.getLanguageIdentifier(), + editorOpts: { + autoClosingBrackets: { + enabledBefore: 'abc', + autoClose: true, + autoWrap: true + } + } + }, (model, cursor) => { + + let autoClosePositions = [ + 'v|ar |a = [|];|', + 'v|ar |b = `|asd`;|', + 'v|ar |c = \'|asd\';|', + 'v|ar d = "|asd";|', + 'v|ar e = /*3*/ 3;|', + 'v|ar f = /** 3 */3;|', + 'v|ar g = (3+5|);|', + 'v|ar h = { |a: \'v|alue\' |};|', + ]; + for (let i = 0, len = autoClosePositions.length; i < len; i++) { + const lineNumber = i + 1; + const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]); + + for (let column = 1; column < autoCloseColumns.length; column++) { + model.forceTokenization(lineNumber); + if (autoCloseColumns[column] === ColumnType.Special1) { + assertType(model, cursor, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); + } else { + assertType(model, cursor, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); + } + } + } + }); + mode.dispose(); + }); + + test('auto-pairing can be disabled', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + 'var a = [];', + 'var b = `asd`;', + 'var c = \'asd\';', + 'var d = "asd";', + 'var e = /*3*/ 3;', + 'var f = /** 3 */3;', + 'var g = (3+5);', + 'var h = { a: \'value\' };', + ], + languageIdentifier: mode.getLanguageIdentifier(), + editorOpts: { + autoClosingBrackets: { + enabledBefore: 'abc', + autoClose: false, + autoWrap: true + }, + autoClosingQuotes: { + enabledBefore: 'abc', + autoClose: false, + autoWrap: true + } + } + }, (model, cursor) => { + + let autoClosePositions = [ + 'var a = [];', + 'var b = `asd`;', + 'var c = \'asd\';', + 'var d = "asd";', + 'var e = /*3*/ 3;', + 'var f = /** 3 */3;', + 'var g = (3+5);', + 'var h = { a: \'value\' };', + ]; + for (let i = 0, len = autoClosePositions.length; i < len; i++) { + const lineNumber = i + 1; + const autoCloseColumns = extractSpecialColumns(model.getLineMaxColumn(lineNumber), autoClosePositions[i]); + + for (let column = 1; column < autoCloseColumns.length; column++) { + model.forceTokenization(lineNumber); + if (autoCloseColumns[column] === ColumnType.Special1) { + assertType(model, cursor, lineNumber, column, '(', '()', `auto closes @ (${lineNumber}, ${column})`); + assertType(model, cursor, lineNumber, column, '"', '""', `auto closes @ (${lineNumber}, ${column})`); + } else { + assertType(model, cursor, lineNumber, column, '(', '(', `does not auto close @ (${lineNumber}, ${column})`); + assertType(model, cursor, lineNumber, column, '"', '"', `does not auto close @ (${lineNumber}, ${column})`); + } + } + } + }); + mode.dispose(); + }); + + test('auto wrapping is configurable', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + 'var a = asd' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (model, cursor) => { + + cursor.setSelections('test', [ + new Selection(1, 1, 1, 4), + new Selection(1, 9, 1, 12), + ]); + + // type a ` + cursorCommand(cursor, H.Type, { text: '`' }, 'keyboard'); + + assert.equal(model.getValue(), '`var` a = `asd`'); + }); + usingCursor({ + text: [ + 'var a = asd' + ], + languageIdentifier: mode.getLanguageIdentifier(), + editorOpts: { + autoClosingBrackets: { + autoWrap: false, + autoClose: true, + enabledBefore: '' + } + } + }, (model, cursor) => { + + cursor.setSelections('test', [ + new Selection(1, 1, 1, 4), + ]); + + // type a ` + cursorCommand(cursor, H.Type, { text: '`' }, 'keyboard'); + + assert.equal(model.getValue(), '` a = asd'); + }); + mode.dispose(); + }); + test('quote', () => { let mode = new AutoClosingMode(); usingCursor({