Repository: microsoft/vscode (TypeScript)
Date: 2021-10-16 01:17:33+00:00
Task type: Bug Fix

Issue:
Backticks not auto-closing for tagged template literals
Issue Type: <b>Bug</b>

When working with tagged template literals in JS and JSX (styled-components is my use case), the backticks used to auto-close when typed, but that recently changed. The big issue though is that if you go into a multi-line literal, and then try to type the closing backtick, vscode inserts two backticks, leaving you with three.

For example if I type:

```
const Button = styled.button`
```

I would expect to have the closing tick automatically inserted and the cursor in between them. Instead it just stops there. If I then go on to enter some css and then manually enter the closing backtick, I get:

```
const Button = styled.button`
  background: papayawhip;
  color: green;
``
```

I then have to manually delete the third backtick, as if I delete the second, the whole pair is deleted because of the auto-close rules.

I have verified this is happening on Mac and Windows 10, with extensions enabled and disabled, and in the Insiders build.

VS Code version: Code 1.28.1 (3368db6750222d319c851f6d90eb619d886e08f5, 2018-10-11T18:07:48.477Z)
OS version: Darwin x64 18.0.0

<details>
<summary>System Info</summary>

|Item|Value|
|---|---|
|CPUs|Intel(R) Core(TM) i7-7820HQ CPU @ 2.90GHz (8 x 2900)|
|GPU Status|2d_canvas: enabled<br>checker_imaging: disabled_off<br>flash_3d: enabled<br>flash_stage3d: enabled<br>flash_stage3d_baseline: enabled<br>gpu_compositing: enabled<br>multiple_raster_threads: enabled_on<br>native_gpu_memory_buffers: enabled<br>rasterization: enabled<br>video_decode: enabled<br>video_encode: enabled<br>webgl: enabled<br>webgl2: enabled|
|Load (avg)|1, 2, 2|
|Memory (System)|16.00GB (0.87GB free)|
|Process Argv|-psn_0_1007862|
|Screen Reader|no|
|VM|17%|

</details><details><summary>Extensions (20)</summary>

Extension|Author (truncated)|Version
---|---|---
ActiveFileInStatusBar|Ros|1.0.3
better-comments|aar|2.0.2
bracket-pair-colorizer|Coe|1.0.60
vscode-eslint|dba|1.6.1
es7-react-js-snippets|dsz|1.8.7
prettier-vscode|esb|1.6.1
dotnet-test-explorer|for|0.5.4
vscode-styled-components|jpo|0.0.22
mssql|ms-|1.4.0
python|ms-|2018.9.0
csharp|ms-|1.16.2
vsliveshare|ms-|0.3.790
indent-rainbow|ode|7.2.4
vscode-docker|Pet|0.3.1
seti-icons|qin|0.1.3
code-settings-sync|Sha|3.1.2
mdx|sil|0.1.0
vscode-status-bar-format-toggle|tom|1.4.0
vim|vsc|0.16.10
quokka-vscode|Wal|1.0.154

(3 theme extensions excluded)

</details>
<!-- generated by issue reporter -->


Issue discussion:
(Experimental duplicate detection)
Thanks for submitting this issue. Please also check if it is already covered by an existing one, like:
- [PHP syntax highlighting breaks with backticks (#59545)](https://www.github.com/Microsoft/vscode/issues/59545) <!-- score: 0.498 -->
<!-- potential_duplicates_comment -->
It seems to me the editor only adds backtick if it's legal for a template string to be in that place. However your example is only legal if styled components are allowed. Could you give an example with a valid js syntax? 

If it only happens with styled component syntax, could be it's a bug (or a lack of feature) in the styled component extension.
It works with just plain ES6 syntax as well. This example:

```
function highlight(strings, ...values) {
    let str = '';
    strings.forEach((string, i) => {
        if (values[i]) {
            str += `${string}<span class='hl'>${values[i]}</span>`;
        } else {
            str += `${string}`;
        }
    })
    return str;
}

const shouldBeHighlighted = 'this should be wrapped in a span';

const markup = highlight`
<div>
  <p>This is some stuff that ${shouldBeHighlighted} but isn't.</p>
</div>
`;

console.log(markup);
```

Is valid ES6, no React/styled-components, and the same issue occurs when trying to type the multi-line literal. 

I should note that manually closing the literal on the same line does work okay, although it doesn't auto-close. It is only on multi-line template literals that the auto-close fires on the last tick.
I'm seeing this bug also in python, where backticks have no special meaning 😞. Sometimes I use them to denote arguments in a user string:

```python
raise ValueError(f'No XML documents found in `{xml_dir}`)
``` 

But when I type the second backtick, vscode inserts two, resulting in:
```f'No XML documents found in `{xml_dir}`|` ```

Strangely, this only happens if there is `}` before inserting the backtick. If I type insert in any other part of the string, the doubling doesn't happen.

@mjbvz should I open a different issue with this problem, to be tagged as a bug? If so, does it belong here or to `vscode-python` ? Thanks ! 
@mjbvz Can this be added for this iteration?
As a workaround, set:

```json
"editor.autoClosingQuotes": "always"
```

Opened https://github.com/Microsoft/vscode/issues/71970 to track possible solution 
> As a workaround, set:
> 
> ```json
> "editor.autoClosingQuotes": "always"
> ```
> 
> Opened #71970 to track possible solution

Thanks for that! 
Anything workaroundish for the cursor coming inside to the next line with an indent on press Enter?

![demo](https://user-images.githubusercontent.com/25827414/80981414-9bb41200-8e32-11ea-9ee1-b631d6bc342a.gif)

FWIW, I found disabling the Babel JavaScript extension put a stop to doubling up on backticks when closing a pair. (However as I rely on that extension, disabling it isn't really an option.)

Solution patch:
diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts
--- a/src/vs/editor/common/controller/cursorTypeOperations.ts
+++ b/src/vs/editor/common/controller/cursorTypeOperations.ts
@@ -598,7 +598,7 @@ export class TypeOperations {
 			}
 
 			// Do not auto-close ' or " after a word character
-			if (autoClosingPair.open.length === 1 && chIsQuote && autoCloseConfig !== 'always') {
+			if (autoClosingPair.open.length === 1 && (ch === '\'' || ch === '"') && autoCloseConfig !== 'always') {
 				const wordSeparators = getMapForWordSeparators(config.wordSeparators);
 				if (insertOpenCharacter && position.column > 1 && wordSeparators.get(lineText.charCodeAt(position.column - 2)) === WordCharacterClass.Regular) {
 					return null;


Test patch:
diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts
--- a/src/vs/editor/test/browser/controller/cursor.test.ts
+++ b/src/vs/editor/test/browser/controller/cursor.test.ts
@@ -4878,6 +4878,18 @@ suite('autoClosingPairs', () => {
 		model.undo();
 	}
 
+	test('issue #61070: backtick (`) should auto-close after a word character', () => {
+		let mode = new AutoClosingMode();
+		usingCursor({
+			text: ['const markup = highlight'],
+			languageId: mode.languageId
+		}, (editor, model, viewModel) => {
+			model.forceTokenization(1);
+			assertType(editor, model, viewModel, 1, 25, '`', '``', `auto closes \` @ (1, 25)`);
+		});
+		mode.dispose();
+	});
+
 	test('open parens: default', () => {
 		let mode = new AutoClosingMode();
 		usingCursor({