Skip to content
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

[regression] Incorrect cursor position after nested completion using $0 placeholder #152837

Closed
HighCommander4 opened this issue Jun 22, 2022 · 9 comments
Assignees
Labels
info-needed Issue requires more information from poster snippets

Comments

@HighCommander4
Copy link

HighCommander4 commented Jun 22, 2022

Notes

  • This is a regression introduced in vscode 1.67 (also affects 1.68 and insiders)
  • The reproduction steps require a language server that produces completion items using the $0 placeholder. The example steps use clangd

Steps to reproduce

(See also screen recordings below)

  1. Install the clangd C++ language server
  2. Start with the following C++ source file:
void moo(int, int);

int main() {
  
}
  1. On line 4, type mo and invoke completion.
  2. Accept the completion item for moo.
  3. Type decl and invoke completion
  4. Accept the completion item for decltype(expression)

Expected results

The cursor ends up at column 26, i.e. just before the closing ) of the inserted decltype(expression).

This is what happens with vscode 1.66:

good

Actual results

The cursor ends up at column 27, i.e. just after the closing ) of the inserted decltype(expression)

This is what happens with vscode 1.67 and later:

bad

Analysis

The bug seems to hinge on the second completion item containing a $0 placeholder -- in this case, the item's insertText is decltype(${0:expression}). If this is changed to decltype(${1:expression}), the bug goes away.

The interpretation of the $0 placeholder is explained in the LSP spec here: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#insertTextFormat

Originally reported at clangd/clangd#1190.

@jrieken
Copy link
Member

jrieken commented Jun 22, 2022

Install the clangd C++ language server

:cough: :cough: minimal steps

a $0 placeholder -- in this case, the item's insertText is decltype(${0:expression}). If this is changed to decltype(${1:expression}), the bug goes away.

$0 is not a placeholder but the final tabstop, e.g it shouldn't be ${0:foobar}but a number >= 1 when you looking for placeholder semantics (doesn't end snippet mode, does repeat placeholder value). Unsure how/if/why that worked before and when that changed but your workaround seems like the correct way of doing it and it is unlikely that we'll support the final tabstop to be a placeholder.

@jrieken jrieken added snippets info-needed Issue requires more information from poster labels Jun 22, 2022
@HighCommander4
Copy link
Author

Install the clangd C++ language server

:cough: :cough: minimal steps

What could I have done to provide more minimal reproduction steps in this case? The only thing that comes to mind is writing a custom language server for the purpose of demonstrating the bug, which doesn't seem like a reasonable bar.

a $0 placeholder -- in this case, the item's insertText is decltype(${0:expression}). If this is changed to decltype(${1:expression}), the bug goes away.

$0 is not a placeholder but the final tabstop, e.g it shouldn't be ${0:foobar}but a number >= 1 when you looking for placeholder semantics (doesn't end snippet mode, does repeat placeholder value). Unsure how/if/why that worked before and when that changed but your workaround seems like the correct way of doing it and it is unlikely that we'll support the final tabstop to be a placeholder.

Thanks, I didn't realize $0 cannot be used as a placeholder (nor, evidently, did the clangd developers who originally wrote this logic). I guess the way to implement the desired semantics (having a placeholder + having the tab keep you inside the parentheses) would be to use decltype(${1:expression}$0) then?

@HighCommander4
Copy link
Author

I guess the way to implement the desired semantics (having a placeholder + having the tab keep you inside the parentheses) would be to use decltype(${1:expression}$0) then?

I guess that's a bit silly, since after you've finished typing the expression, your cursor is already where the $0 is and pressing <tab> to stay in the same place is pointless.

I think the preferred behaviour would be the one decltype(${0:expression}) had with vscode <= 1.66: after you type the expression, if you're inside a nested completion, a <tab> takes you directly to the next tab stop of the enclosing completion. Is there a way to achieve this behaviour in vscode >= 1.67?

@kadircet
Copy link

$0 is not a placeholder but the final tabstop

reading the spec:

Tab stops
With tab stops, you can make the editor cursor move inside a snippet. Use $1, $2 to specify cursor locations. The number is the order in which tab stops will be visited, whereas $0 denotes the final cursor position. Multiple tab stops are linked and updated in sync.

Placeholders
Placeholders are tab stops with values, like ${1:foo}. The placeholder text will be inserted and selected such that it can be easily changed. Placeholders can be nested, like ${1:another ${2:placeholder}}.

I don't see why $0 can't be a placeholder while other tabstops can be. I guess $0 isn't even a tabstop?
It would be great to reflect that in the LSP docs as well, the wording you used are actually more common in the spec ($0 defines the final tab stop, it defaults to the end of the snippet.) which doesn't exclude the possibility of it being a placeholder.

I can see how this might've been the decision made, but as @HighCommander4 pointed out, this behavior actually helped language servers (well, at least clangd) to implement some functionality. It'd be great to hear that there'll still be a way to support these without requiring extra tabs.

@vscodenpa
Copy link

Hey @jrieken, this issue might need further attention.

@HighCommander4, you can help us out by closing this issue if the problem no longer exists, or adding more information.

@HighCommander4
Copy link
Author

While we have worked around this in clangd for now, it would be nice to consider the behaviour change described in this comment:

I think the preferred behaviour would be the one decltype(${0:expression}) had with vscode <= 1.66: after you type the expression, if you're inside a nested completion, a <tab> takes you directly to the next tab stop of the enclosing completion. Is there a way to achieve this behaviour in vscode >= 1.67?

Or alternatively to update the LSP docs as described in this comment to clarify that $0 cannot be a placeholder.

@vscodenpa
Copy link

Hey @jrieken, this issue might need further attention.

@HighCommander4, you can help us out by closing this issue if the problem no longer exists, or adding more information.

@vscodenpa
Copy link

This issue has been closed automatically because it needs more information and has not had recent activity. See also our issue reporting guidelines.

Happy Coding!

@vscodenpa vscodenpa closed this as not planned Won't fix, can't repro, duplicate, stale Dec 14, 2022
@HighCommander4
Copy link
Author

@HighCommander4, you can help us out by closing this issue if the problem no longer exists, or adding more information.

It's not clear what "more information" was being asked for here.

The current status of the bug is as I summarized in this previous comment. I believe it remains valid.

@github-actions github-actions bot locked and limited conversation to collaborators Jan 28, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
info-needed Issue requires more information from poster snippets
Projects
None yet
Development

No branches or pull requests

4 participants