Change:
<!-- Do Not Delete This! bugreporttemplate --<!-- Please read our Rules of Conduct: --<!-- Please search existing issues to avoid creating duplicates. --<!-- Also please test using the latest insiders build to make sure your issue has not already been fixed: --<!-- Use Help Report Issue to prefill these. --Version: 1.49.0-insider Commit: Date: 2020-08-17T17:11:16.078Z (1 day ago) Electron: 9.2.0 Chrome: 83.0.4103.122 12.14.1 V8: 8.3.110.13-electron.0 OS: Darwin x64 19.6.0 Steps to Reproduce: Add the following python snippet: or PowerShell snippet: : which will cause auto-closing pair... (at this point you have where is the cursor) add a or and hit TAB or ENTER on the intellisense item NOTE THE TRAILING . Snippets and auto-closing pairs should play nice together so that the whole experience generates valid code. <!-- Launch with to check. --Does this issue occur when all extensions are disabled?: Yes
Here's a javascript example for array destructuring: which gets you: when you type:
better example! A javascript example for : which gets you: when you type:
for verification, this: should get you: on insert mode replace (default is SHIFT + ENTER)
I'll note that it works fine in non-json files, but is reversed in a json file (as are the insert/replace hints in the suggestion box). W10
that seems like a separate issue not related to this fix. Can you open a new issue?
Yes, I'll open another issue.

Discussion:
import { Snippet, SnippetSource } from 'vs/workbench/contrib/snippets/browser/snippetsFile'; import { isPatternInWord } from 'vs/base/common/filters'; import { StopWatch } from 'vs/base/common/stopwatch'; <ins> import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; </ins> export class SnippetCompletion implements CompletionItem {

const prefixPos = position.column - (1 + start); const prefixRestLen = snippet.prefixLow.length - prefixPos; const endsWithPrefixRest = compareSubstring(lineContent, snippet.prefixLow, columnOffset, (columnOffset) + prefixRestLen, prefixPos, prefixPos + prefixRestLen); <del> const endColumn = endsWithPrefixRest === 0 ? position.column + prefixRestLen : position.column; const replace = Range.fromPositions(position.delta(0, -prefixPos), { lineNumber: position.lineNumber, column: endColumn }); </del> <ins> const startPosition = position.delta(0, -prefixPos); let endColumn = endsWithPrefixRest === 0 ? position.column + prefixRestLen : position.column; // First check if there is anything to the right of the cursor if (columnOffset < lineContent.length) { const autoClosingPairs = LanguageConfigurationRegistry.getAutoClosingPairs(languageId); const standardAutoClosingPairConditionals = autoClosingPairs.autoClosingPairsCloseSingleChar.get(lineContent[columnOffset]); // If the character to the right of the cursor is a closing character of an autoclosing pair if (standardAutoClosingPairConditionals?.some(p => // and the start position is the opening character of an autoclosing pair p.open === lineContent[startPosition.column - 1] && // and the snippet prefix contains the opening and closing pair at its edges snippet.prefix.startsWith(p.open) && snippet.prefix[snippet.prefix.length - 1] === p.close)) { // Eat the character that was likely inserted because of auto-closing pairs endColumn++; } } const replace = Range.fromPositions(startPosition, { lineNumber: position.lineNumber, column: endColumn }); </ins> const insert = replace.setEndPosition(position.lineNumber, position.column); suggestions.push(new SnippetCompletion(snippet, { replace, insert }));

import { Snippet, SnippetSource } from 'vs/workbench/contrib/snippets/browser/snippetsFile'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { CompletionContext, CompletionTriggerKind } from 'vs/editor/common/modes'; <ins> import { DisposableStore } from 'vs/base/common/lifecycle'; </ins> class SimpleSnippetService implements ISnippetsService { declare readonly _serviceBrand: undefined;

} suite('SnippetsService', function () { <ins> const disposableStore: DisposableStore = new DisposableStore(); const context: CompletionContext = { triggerKind: CompletionTriggerKind.Invoke }; </ins> suiteSetup(function () { ModesRegistry.registerLanguage({

}); }); <ins> suiteTeardown(function () { disposableStore.dispose(); }); </ins> let modeService: ModeServiceImpl; let snippetService: ISnippetsService; <del> let context: CompletionContext = { triggerKind: CompletionTriggerKind.Invoke }; </del> setup(function () { modeService = new ModeServiceImpl();

)]); }); test('snippet completions - simple', function () { const provider = new SnippetCompletionProvider(modeService, snippetService);

}); test('issue #61296: VS code freezes when editing CSS file with emoji', async function () { <del> let toDispose = LanguageConfigurationRegistry.register(modeService.getLanguageIdentifier('fooLang')!, { </del> <ins> disposableStore.add(LanguageConfigurationRegistry.register(modeService.getLanguageIdentifier('fooLang')!, { </ins> wordPattern: /(#?-?d*.dw*%?)|(::?[w-]*(?=[^,{;]*[,{]))|(([@#.!])?[w-?]+%?|[@#!.])/g <del> }); </del> <ins> })); </ins> snippetService = new SimpleSnippetService([new Snippet( ['fooLang'], 'bug',

let result = await provider.provideCompletionItems(model, new Position(1, 8), context)!; assert.equal(result.suggestions.length, 1); <del> toDispose.dispose(); </del> }); test('No snippets shown when triggering completions at whitespace on line that already has text #62335', async function () {

assert.equal((first.range as any).insert.endColumn, 9); assert.equal((first.range as any).replace.endColumn, 9); }); <ins> test('Snippet will replace auto-closing pair if specified in prefix', async function () { disposableStore.add(LanguageConfigurationRegistry.register(modeService.getLanguageIdentifier('fooLang')!, { brackets: [ ['{', '}'], ['[', ']'], ['(', ')'], ] })); snippetService = new SimpleSnippetService([new Snippet( ['fooLang'], 'PSCustomObject', '[PSCustomObject]', '', '[PSCustomObject] @{ Key = Value }', '', SnippetSource.User )]); const provider = new SnippetCompletionProvider(modeService, snippetService); let model = createTextModel('[psc]', undefined, modeService.getLanguageIdentifier('fooLang')); let result = await provider.provideCompletionItems(model, new Position(1, 5), context)!; assert.strictEqual(result.suggestions.length, 1); let [first] = result.suggestions; assert.strictEqual((first.range as any).insert.endColumn, 5); // This is 6 because it should eat the `]` at the end of the text even if cursor is before it assert.strictEqual((first.range as any).replace.endColumn, 6); }); </ins> });