diff --git a/src/vs/editor/contrib/snippet/browser/snippetParser.ts b/src/vs/editor/contrib/snippet/browser/snippetParser.ts index da4b4629d7d5b..f262ddf34dcfd 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetParser.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetParser.ts @@ -165,6 +165,13 @@ export abstract class Marker { return this._children; } + get rightMostDescendant(): Marker { + if (this._children.length > 0) { + return this._children[this._children.length - 1].rightMostDescendant; + } + return this; + } + get snippet(): TextmateSnippet | undefined { let candidate: Marker = this; while (true) { diff --git a/src/vs/editor/contrib/snippet/browser/snippetSession.ts b/src/vs/editor/contrib/snippet/browser/snippetSession.ts index a25ee16b15ca0..c50df9d79142b 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetSession.ts @@ -208,9 +208,23 @@ export class OneSnippet { return this._snippet.placeholders.length > 0; } + /** + * A snippet is trivial when it has no placeholder or only a final placeholder at + * its very end + */ get isTrivialSnippet(): boolean { - return this._snippet.placeholders.length === 0 - || (this._snippet.placeholders.length === 1 && this._snippet.placeholders[0].isFinalTabstop); + if (this._snippet.placeholders.length === 0) { + return true; + } + if (this._snippet.placeholders.length === 1) { + const [placeholder] = this._snippet.placeholders; + if (placeholder.isFinalTabstop) { + if (this._snippet.rightMostDescendant === placeholder) { + return true; + } + } + } + return false; } computePossibleSelections() { diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts index fce4d2c432bd1..4279ca422a481 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts @@ -692,4 +692,18 @@ suite('SnippetController2', function () { assert.deepStrictEqual(editor.getSelections(), [new Selection(1, 5, 1, 5), new Selection(1, 10, 1, 10), new Selection(2, 5, 2, 5), new Selection(2, 10, 2, 10)]); }); }); + + test('Bug: cursor position $0 with user snippets #163808', function () { + + const ctrl = instaService.createInstance(SnippetController2, editor); + model.setValue(''); + + ctrl.insert('\n \n$0"\n'); + assert.deepStrictEqual(editor.getSelections(), [new Selection(1, 23, 1, 23)]); + + ctrl.insert('Qualifier="$0"'); + assert.strictEqual(model.getValue(), '\n \n"\n'); + assert.deepStrictEqual(editor.getSelections(), [new Selection(1, 34, 1, 34)]); + + }); });