Skip to content

Snippets and Auto-closing pairs play nicer #114235

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

TylerLeonhardt
Copy link
Member

@TylerLeonhardt TylerLeonhardt commented Jan 12, 2021

This PR fixes #104952

Previously, auto-closing pairs would get in the way of some snippets if the snippet's prefix started with an autoclosing pair (like [). Here's an example:

"PSCustomObject": {
        "prefix":"[PSCustomObject",
        "body":[
            "[PSCustomObject] @{ Key = Value }"
        ]
    }

To fix/workaround this "odd behavior" (from an end user perspective) this PR uses the following logic to decide whether or not the snippet eats the character immidiately to the right of the cursor:

  1. First check if there is anything to the right of the cursor
  2. Then check If the character to the right of the cursor is a closing character of an autoclosing pair for this language
  3. Then check if the start position in the file is the opening character of an autoclosing pair
  4. Finally check if the snippet prefix contains the opening and closing pair at its edges

If this is satified, we say that the end of the snippet is endColumn++ thus eating the character to the right of the cursor.

This requires that the snippet more or less "opts-in" to this behavior by making sure that the prefix has autoclosing pairs around it... so if I wanted to opt-in the above snippet, I would change it to this:

"PSCustomObject": {
        "prefix":"[PSCustomObject]",
        "body":[
            "[PSCustomObject] @{ Key = Value }"
        ]
    }

Note the extra ] in the prefix

Then, and only then will the eat the ] of the auto-closing pair and only if the user uses replace for the suggestion (SHIFT+ENTER by default) instead of the insert behavior of just ENTER. If the user wants ENTER to have this behavior, they can apply the following setting:

"editor.suggest.insertMode": "replace"

which is made exactly for this purpose.

@TylerLeonhardt TylerLeonhardt self-assigned this Jan 13, 2021
@TylerLeonhardt TylerLeonhardt changed the title Delay animating until content is prepared to prevent weird flying elements. Snippets and Auto-closing pairs play nicer Jan 13, 2021
@@ -43,11 +44,13 @@ suite('SnippetsService', function () {
});
});

let disposables: Array<IDisposable>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: We have a DisposableStore which makes this a little simpler

teardown(function () {
for (const disposable of disposables) {
disposable.dispose();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would then be store.clear()

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refactored this a bit so there's only one DisposableStore for the whole test suite and then I dispose it in suiteTeardown

@TylerLeonhardt TylerLeonhardt merged commit d6936dd into master Jan 13, 2021
@TylerLeonhardt TylerLeonhardt deleted the TylerLeonhardt/snippets-and-autoclosing-pairs-play-nicer branch January 13, 2021 20:29
@ArturoDent
Copy link

In testing this snippet:

"PSCustomObject": {
        "prefix":"[PSCustomObject]",
        "body":[
            "[PSCustomObject] @{ Key = Value }"
        ]
 }

Here are my results with suggest.insertMode at the default insert:

[PSCustomObject] @{ Key = Value }    // via tab, insertMode: default insert

[PSCustomObject] @{ Key = Value }    // via enter, insertMode: default insert  <==== ??

[PSCustomObject] @{ Key = Value }]   // via shift-enter, insertMode: default insert

So I am confused by the statement above:

"Then, and only then will the eat the ] of the auto-closing pair and only if the user uses replace for the suggestion (SHIFT+ENTER by default) instead of the insert behavior of just ENTER."

As you can see the end auto-closing pair was eaten for me using Enter and default suggest.insertMode of insert. I like this behaviour but it seems to conflict with the quoted description??

I get the exact same results with suggest.insertMode set to replace:

 [PSCustomObject] @{ Key = Value }  // via tab, insertMode: replace

[PSCustomObject] @{ Key = Value }  // via enter, insertMode: replace

[PSCustomObject] @{ Key = Value }] // via shift-enter, insertMode: replace

Am I missing something or is the description inaccurate?

@TylerLeonhardt
Copy link
Member Author

@ArturoDent are you sure your insertMode wasn't set to replace somewhere else? Because the default for Shift+Enter is replace....unless you set insertMode to replace in which case Shift+Enter does insert.

@ArturoDent
Copy link

suggest.insertMode is at the default insert:

autoClosePairSnippet


Version: 1.53.0-insider (user setup)
Commit: c265dff
Date: 2021-01-14T06:31:42.082Z
Electron: 11.2.0
Chrome: 87.0.4280.141
Node.js: 12.18.3
V8: 8.7.220.31-electron.0
OS: Windows_NT x64 10.0.21286

@TylerLeonhardt
Copy link
Member Author

Look at the suggestion tooltip in your gif. It says "ENTER" for Replace and SHIFT+ENTER for Insert.

Maybe you have a workspace setting?

@ArturoDent
Copy link

I have no workspace settings in that workspace.

@ArturoDent
Copy link

ArturoDent commented Jan 15, 2021

Here is a full list of results for a JSON file. One set with both user and workspace sugget.insertMode set to insert and another with suggest.insertMode set to replace.

You see I tried both triggering the snippet with [psc] and psc - there was a different result only for Shift+Enter - should that be the case?

  "PSCustomObject": {
	"prefix": "[PSCustomObject]",
	"body": [
		"[PSCustomObject] @{ Key = Value }"
	]
},

 [PSCustomObject] @{ Key = Value }     // tab, insertMode = insert, with [psc]
 [PSCustomObject] @{ Key = Value }     // tab, insertMode = replace, with [psc]

 [PSCustomObject] @{ Key = Value }     // tab, insertMode = insert, with psc
 [PSCustomObject] @{ Key = Value }     // tab, insertMode = replace, with psc

 [PSCustomObject] @{ Key = Value }     // enter, insertMode = insert, with [psc]
 [PSCustomObject] @{ Key = Value }     // enter, insertMode = replace, with [psc]

 [PSCustomObject] @{ Key = Value }     // enter, insertMode = insert, with psc
 [PSCustomObject] @{ Key = Value }     // enter, insertMode = replace, with psc

 [PSCustomObject] @{ Key = Value }]    // shift+enter, insertMode = insert, with [psc]
 [PSCustomObject] @{ Key = Value }]    // shift+enter, insertMode = replace, with [psc]

 [PSCustomObject] @{ Key = Value }     // shift+enter, insertMode = insert, with psc
 [PSCustomObject] @{ Key = Value }     // shift+enter, insertMode = replace, with psc

Here are the results for a non-JSON file in fact a javascript file):

[PSCustomObject] @{ Key = Value }]  // enter, insert [psc]
[PSCustomObject] @{ Key = Value }   // enter, insert, psc

[PSCustomObject] @{ Key = Value }   // shift+enter, insert [psc]
[PSCustomObject] @{ Key = Value }   // shift+enter, insert psc

[PSCustomObject] @{ Key = Value }]  // tab, insert [psc]
[PSCustomObject] @{ Key = Value }   // tab, insert psc

[PSCustomObject] @{ Key = Value }]  // enter, replace [psc]
[PSCustomObject] @{ Key = Value }   // enter, replace psc

[PSCustomObject] @{ Key = Value }]  // shift+enter, replace [psc]
[PSCustomObject] @{ Key = Value }   // shift+enter, replace psc

[PSCustomObject] @{ Key = Value }   // tab, replace [psc]
[PSCustomObject] @{ Key = Value }   // tab, replace psc

The second set of results is probably more in line with what you were expecting - although note there is no difference between insert and replace modes using the psc trigger whereas there are differences when using the [psc] trigger.

I know JSON completion is trickier because what is treated as a word is different in those files.

@TylerLeonhardt
Copy link
Member Author

TylerLeonhardt commented Jan 15, 2021

Shift+Enter and Enter/Tab should always have opposite behavior.

If "editor.suggest.insertMode": "insert":

Enter should still show the ] at the end
Shift+Enter should not

If "editor.suggest.insertMode": "replace":

Shift+Enter should still show the ] at the end
Enter should not

That is what is shown in the suggest status bar:
image

It seems like the problem here is that when you have "editor.suggest.insertMode": "insert" the expected behavior of that (Enter doing insert, Shift+Enter doing replace) is not being honored. This even applies to other snippets without autoclosing pairs:

        "sdf": {
		"prefix": "asdf",
		"body": "ffffffff"
	}

I don't think this PR caused this behavior but it's certainly suspicious.

Can you confirm that you are seeing this as well?

@ArturoDent
Copy link

Using

    "sdf": {
	"prefix": "asdf",
	"body": "ffffffff"
}

and "editor.suggest.insertMode": "insert"

The only way I have found to actually insert and not always replace is the third example in the following demo. Where I actually had to Esc to close the suggestion widget, reopen with Ctrl+Space and then Enter or mouse-clicking the Insert hint does seem to work. I say "seem" because while the fffffff is inserted at the cursor position the preceding part of the prefix, say a is replaced. I am not sure whether that is expected behaviour?

autoClosePairSnippet2

@TylerLeonhardt
Copy link
Member Author

I am not sure whether that is expected behaviour?

Yes that all looks right. Keep in mind that "replace" behavior only decides if the content to the right of the cursor is included in the prefix on the left of the cursor.

Everything to the left of the cursor is always replaced.

@github-actions github-actions bot locked and limited conversation to collaborators Feb 27, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Snippets + Auto-closing pairs don't compliment each other
3 participants