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

Clarify whether $0 can be a placeholder (${0:foo}) #1946

Open
DanTup opened this issue Jun 9, 2024 · 9 comments
Open

Clarify whether $0 can be a placeholder (${0:foo}) #1946

DanTup opened this issue Jun 9, 2024 · 9 comments

Comments

@DanTup
Copy link
Contributor

DanTup commented Jun 9, 2024

Currently the spec says that snippets can use "tab stops and placeholders", where a tabstop looks like $1 and a placeholder looks like ${1:foo}.

It also says that "$0 defines the final tab stop" but it does not make it clear whether $0 can be used with placeholder text (like ${0:foo}).

Using it like this works fine in VS Code (it treats it as a final tabstop and a placeholder, so the text is selected and snippet mode is exited), however nevoim's LSP client is behaving differently and it could be argued that the spec doesn't say that $0 is allowed to be a placeholder.

@dbaeumer assuming VS Code's behaviour is desired, I can send a PR to change:

$0 defines the final tab stop

to

$0 defines the final tab stop or placeholder

Does this seem reasonable?

@HighCommander4
Copy link

For what it's worth, when we ran into this in clangd I filed microsoft/vscode#152837, and received the following guidance there:

$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.

and we subsequently changed clangd to avoid using $0 as a placeholder.

@HighCommander4
Copy link

(That said, from a user's point of view, I think it would be nicer if $0 being a placeholder was allowed / supported.)

@DanTup
Copy link
Contributor Author

DanTup commented Jul 3, 2024

Thanks, that's interesting. The fact that this has come up before, and that VS Code's current behaviour doesn't seem to match what's written there (as far as I can tell it is working because Dart is currently using it and it's fine in VS Code but not working in neovim) I think shows that this needs to be made clear in the LSP spec.

I also agree that it would be better to support it than not, I don't see any reason why the final stop should not be somewhere that can have placeholder text, it'll just results in a worse experience for seemingly no benefit 😔

@mfussenegger
Copy link

I also agree that it would be better to support it than not, I don't see any reason why the final stop should not be somewhere that can have placeholder text, it'll just results in a worse experience for seemingly no benefit

It allows a client to exit the snippet mode more reliably. If you have a text on the last placeholder the client must remain in select mode. This may be subjective, but my preference is that $0 is a clear stop without being a placeholder.

If it were a regular placeholder I'd also question why a special $0 is needed at all. You could just go with the next number.

@DanTup
Copy link
Contributor Author

DanTup commented Jul 3, 2024

If you have a text on the last placeholder the client must remain in select mode.

I'm not entirely sure what "select mode" means here. It it means "the text is selected" then that sounds like desired behaviour. If it means another mode that allows tabbing between placeholders, then I don't see why it can't be exited. The goal here is simply to have some placeholder text at the last tabstop location that is selected to allow the user to type over. It seems odd to say "you can have placeholder text for all tabstop locations except for the very last one which must have no text".

If it were a regular placeholder I'd also question why a special $0 is needed at all. You could just go with the next number.

I agree, but VS Code will force a $0 onto the end of the snippet if we don't use $0 ourselves. If we could prevent that, I would also be happy :)

@rwols
Copy link
Contributor

rwols commented Jul 3, 2024

In Sublime Text, you enter a "snippet mode" when applying a snippet. The $1, $2, $3, ... placeholders can be tabbed through (with the tab key, or going backwards with shift+tab). The $0 is meant to exit "snippet mode".

The behavior of VSCode seems right to me: the user has to be able to exit snippet mode in some way so it appends $0 if there's no $0 at the end.

Making $0 a placeholder doesn't make sense to me because it serves a different functionality than the placeholders.

If you were to change the spec for this it has to be an opt-in capability for the client.

@DanTup
Copy link
Contributor Author

DanTup commented Jul 3, 2024

I don't understand why these two things should be tied together. There are two concepts here:

  1. Tabstops - the locations that you can navigate between when in "snippet mode"
  2. Placeholder text - text shown at the location of a tabstop that is automatically selected ready to type over when you navigate to that tabstop

It's not clear to me why "the final tabstop that exits snippet mode" should be forbidden from having placeholder text. It's an arbitrary limitation that just prevents some ux that is better in some cases. VS Code already supports is today, and at least two languages have tried to use it (Dart does use it, and clangd used it but stopped because VS Code apparently didn't handle it properly before - though I assume that has changed since it's working well for Dart in VS Code).

If you were to change the spec for this it has to be an opt-in capability for the client.

That would be fine by me, although since the current spec is ambiguous and usually the spec is clarified to match VS Code's behaviour, I don't know if that's likely.

I think clarifying the spec to say $0 can't have placeholder text and having no ability to use it would be a step backwards though, because it's in use and supported by VS Code today, and disallowing it will worse then experience in some cases where it's being used (where we don't want the final stop to be at the end of the string, and we want to include placeholder text).

@mfussenegger
Copy link

mfussenegger commented Jul 3, 2024

It's not clear to me why "the final tabstop that exits snippet mode" should be forbidden from having placeholder text. It's an arbitrary limitation that just prevents some ux that is better in some cases.

It gives users a predictable model: If text is selected you're in snippet editing mode and a tab moves to the next tabstop which might be the last.

If the last tabstop can have a placeholder too, the editor can choose to either:

  • Still support tab to end the select/replace mode. In which case you get the same as if you used ${4:foo}$0
  • Have tab insert the tab instead of jumping again, which makes for confusing behavior as it is harder for users to tell what happens. In modal editors like neovim this would also imply that they'd need to switch out of select/replace mode, and enter insert mode again if they want to continue typing. This is imho more disruptive then the first option.

I struggle to see how this is an UX improvement?

@DanTup
Copy link
Contributor Author

DanTup commented Jul 3, 2024

It gives users a predictable model: If text is selected you're in snippet editing mode and a tab moves to the next tabstop which might be the last.

This doesn't feel like much of a benefit to me. If there are other tabstops (that aren't placeholders) then it may be the case that you're in snippet editing mode even when there is no selection, so it seems to me like the indication that you're in snippet editing mode needs to be clear in some way other than whether there is text selected.

I struggle to see how this is an UX improvement?

That's because neither of your options are what's being asked for :-) What I'd like is what VS Code already does today, which is to tab to the last tabstop(placeholder), exit snippet mode, and have the placeholder text selected.

It's valid to want to have the final tabstop not at the end of the string (because otherwise $0 would always be implied at the end and you wouldn't be able to move it). I don't understand why it's an odd thing to want that behaviour, but to show some placeholder text at that location.

The initial reason this came up for Dart/Flutter was that users want to break out of snippet edit mode at a location that is not the end. They would complete something like Container(${1:child}) and VS Code would remain in snippet mode where code completion would behave differently and they would get highlighting of all code they type:

ca3dae8e-b331-4c07-8b41-964877854c81

So we changed to $0 so that it would exit snippet mode. I don't see why we should say that having placeholder text is any less valid when doing this.

Now it's something like this... the final tabstop has placeholder text, but when you get to it snippet mode is exited and doesn't get in the way of you writing more code, even if it's multiple lines:

snippet.mp4

This is the behaviour of Dart today in VS Code. IMO it would be better for LSP to continue to support this functionality (which some servers and VS Code are already using) than to take it away. If servers or editors don't want to use it, they don't have to - but it's a step backwards to take it away IMO.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants