Skip to content

add more completion about "impl" #19447

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
merged 3 commits into from
Apr 5, 2025

Conversation

Natural-selection1
Copy link
Contributor

Completed: #19442

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Mar 25, 2025
@ChayimFriedman2
Copy link
Contributor

I wonder if these should be without spaces/angle brackets, since users cannot type them to filter the completions list?

@Natural-selection1
Copy link
Contributor Author

Natural-selection1 commented Mar 27, 2025

I wonder if these should be without spaces/angle brackets, since users cannot type them to filter the completions list?

I don't have strong opinions about spaces, since we already have completion items like "while let" (unless we consider it outdated design).
image

I think impl is an extremely special block. When you want to write it, just typing "im" should be enough to lock it in.
image

As for angle brackets, I do think it requires careful consideration. But when I realized we also have completion items like "pub(crate)", I believe the reasoning is similar. When we write "pub"/"impl" (and their variants), just typing "pu"/"im" should be sufficient to lock them in. Perhaps the only thing to consider is "how to distinguish between 'impl<> for <>' and 'impl for' without requiring the use of ↑↓ arrows."(In fact, we can distinguish them by "im "(two Spaces), but that's weird, isn't it)

@Veykril
Copy link
Member

Veykril commented Mar 27, 2025

I wonder if these should be without spaces/angle brackets, since users cannot type them to filter the completions list?

That;s a good thing to check, I would not be surprised if editors have differing behavior for spaces here (though its also relevant what our capabilities register for completion characters iirc)

@Natural-selection1
Copy link
Contributor Author

Any definitive naming scheme?
Perhaps the most important thing right now is to modify/add ergonomic characters to distinguish between "impl for" and "impl<> for <>" (although in my opinion, whitespace would be a good choice, since we've had completions like "while let"/"else if"/"if let" for three years without any issues being reported about them).

@flodiebold
Copy link
Member

I'm not convinced it's worth adding more of these. Especially for the <> one, I'd rather have an assist that automatically adds a type parameter? Like you type impl Foo<T|>, trigger the assist and it adds the <T> after impl.

@Natural-selection1
Copy link
Contributor Author

I'm not convinced it's worth adding more of these. Especially for the <> one, I'd rather have an assist that automatically adds a type parameter? Like you type impl Foo<T|>, trigger the assist and it adds the <T> after impl.

Although not strictly enforced (e.g., impl<T> fmt::Debug for IterMut<'_, T>),
the impl<> tends to be a superset of the generic parameters and lifetime parameters in Foo<>.

impl<T, U> Foo<T> {
    fn convert<U>(&self) -> Foo<U> {}
}

Moreover, if we don't use where clauses to specify bounds,
we would annotate them in impl<> instead.

impl<T: PartialEq, A: Allocator> PartialEq for LinkedList<T, A>

If we do need to specify bounds in impl<> (which is not uncommon),
such auxiliary functionality wouldn't become much better than "impl<$4> $1 for $2<$3>"

@Natural-selection1
Copy link
Contributor Author

This PR is based on the following observations:

  • We typically write impl Foo very few times but write impl Some for Foo many times.
    Therefore, I added "impl for" (impl $1 for $2).

  • For non-generic data containers, there are also implementations like impl<'a, T> for Foo.
  • Deleting a "shifted char" is easier than writing it.
  • For generic data containers, the generic and lifetime parameters in impl<> tend to always be a superset of the non-concrete generic and lifetime parameters in Foo<>.
  • If where is not used, we annotate bounds in impl<> (which is extremely common for non-closure generic parameters).

Thus, what I chose to add is "impl<> for <>" (impl<$4> $1 for $2<$3>) instead of (impl<$3> $1 for $2<$3>).


I don’t think blank <> is difficult to accept,
since we already have completions like while let/if let/pub(crate)/pub(super),
which contain () as shifted characters and blank.
The only problem, in my opinion, is how to distinguish between impl<> for <> and impl for without requiring the use of ↑↓ arrows and blank.

With the current approach:

  • im Tabimpl
  • im Blank Tabimpl for
  • im Blank Blank Tabimpl<> for <>

Maybe change impl<> for <> to impl for with param?

Additionally, adjusting the cursor jump order? for example, changing:

  • impl $1 for $2 to impl $2 for $1
  • impl<$4> $1 for $2<$3> to impl<$4> $3 for $1<$2> (or another solution)
    —to make it more aligned with the writing logic.

I wonder if my argument is persuasive?

@Veykril
Copy link
Member

Veykril commented Mar 28, 2025

I feel like a better workflow (that we should enable) would be the following maybe?:

  • write impl / get a impl $0 {} completion
  • start writing a trait name -> get a Trait<$1, $2, ...> for $0 completion (with each required trait arg as a tabstop), that is we now have the following state impl Trait<$1, $2, ...> for $0 {}
  • then offer assists (diagnostic quickfix) for adding unresolved name segments as a new generic parameter to the surrounding impl (this one we should really have either way, its so useful)

That is, I feel like the user shouldn't even need to jump back to the generic parameter listing in the first place. If you create a generic parameter there, you will have to use it anyways and so typing out the reference to it first and then have r-a just create the definition for you sounds more ergonomic to me?

To be clear, I do agree there is an ergonomics gap here. Its just tabstop snippet completions can be a lot more annoying than helpful depending on how they are designed, and duplicating completions to accomodate for a variety of snippet setups can be more confusing than helpful which is why I (and presumably Florian) are a bit wary of this here.

@Natural-selection1
Copy link
Contributor Author

which is why I (and presumably Florian) are a bit wary of this here.

I fully understand the caution from you and other members—those would be new perspectives for me to consider (and they are usually better and more comprehensive).

So, at this stage, would just adding the "impl for" be acceptable?

@Veykril
Copy link
Member

Veykril commented Mar 28, 2025

Just the impl for is probably fine for now

@Natural-selection1
Copy link
Contributor Author

By the way, I found some "inconsistency problem" here, and its author seems to use blank to do the same thing as $0. Do I fix this minor issue directly here or open a new PR?

if wants_raw_token {
add_keyword("raw", "raw ");
}
if wants_const_token {
add_keyword("const", "const ");
}
if wants_mut_token {
add_keyword("mut", "mut ");
}

@Veykril Veykril added this pull request to the merge queue Apr 5, 2025
@Veykril
Copy link
Member

Veykril commented Apr 5, 2025

Those probably want to use snippets as well yes

Merged via the queue into rust-lang:master with commit bec5459 Apr 5, 2025
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants