Skip to content

[ty] Double click to insert inlay hint#21600

Merged
Gankra merged 10 commits intoastral-sh:mainfrom
MatthewMckee4:bake-inlay-hints
Nov 24, 2025
Merged

[ty] Double click to insert inlay hint#21600
Gankra merged 10 commits intoastral-sh:mainfrom
MatthewMckee4:bake-inlay-hints

Conversation

@MatthewMckee4
Copy link
Contributor

Summary

Resolves astral-sh/ty#317 (comment).

I can't get the auto import working great.

I haven't added many places where we specify that the type display is invalid syntax.

Test Plan

Nothing yet

@astral-sh-bot
Copy link

astral-sh-bot bot commented Nov 24, 2025

Diagnostic diff on typing conformance tests

No changes detected when running ty on typing conformance tests ✅

@astral-sh-bot
Copy link

astral-sh-bot bot commented Nov 24, 2025

mypy_primer results

No ecosystem changes detected ✅

No memory usage changes detected ✅

Comment on lines 108 to 110
let Some(definition_name) = definition.name(db) else {
continue;
};
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This causes issues for some definition kinds, like Unknown.

@AlexWaygood AlexWaygood changed the title Double click to insert inlay hint [ty] Double click to insert inlay hint Nov 24, 2025
@AlexWaygood AlexWaygood added server Related to the LSP server ty Multi-file analysis & type inference labels Nov 24, 2025
@Gankra
Copy link
Contributor

Gankra commented Nov 24, 2025

The parts that aren't the auto-import stuff look reasonable to me (just not as familiar with auto-import stuff).

Might make sense to have the auto-import stuff be a follow-up?

@MatthewMckee4
Copy link
Contributor Author

Yes, I agree. Perhaps @BurntSushi would be able to advise or help with the auto import?

I can remove the auto import for now.

Perhaps we can put this behind a experimental flag for now also?

I will also try to add more invalid syntax places.

I also think it would be useful if we had alternative display for types that emit invalid synatx, like I'd still like to add a function signature with Callable notation.
This also seems like something we can just iterate on.

@Gankra
Copy link
Contributor

Gankra commented Nov 24, 2025

Perhaps we can put this behind a experimental flag for now also?

I'm on the fence about it because you can just... not double-click (and undo if you do). I think we can land the minimal implementation and then disable/flag it if it's a problem for some reason.

This also seems like something we can just iterate on.

Yeah agreed.

@Gankra
Copy link
Contributor

Gankra commented Nov 24, 2025

I don't think it's strictly a blocker to not catch all the invalid syntax. It's something we'll probably burn down really fast in followups and the only stakes is a user getting mad that a feature they manually invoked did something useless.

@Gankra
Copy link
Contributor

Gankra commented Nov 24, 2025

Actually ok I think it's slightly higher stakes -- "user gets very confused" is probably more likely. But still, followups.

@MatthewMckee4
Copy link
Contributor Author

I'm on the fence about it because you can just... not double-click

true, but at least for now if you double click on some type with a todo type, its looks horribly and not an amazing experience.

Though, we may not get the user feedback about missing invalid syntax types if we hid it.

Happy to keep it open now, and let the issues trickle in.

new_text: (*content).to_string(),
});
}
}
Copy link
Member

Choose a reason for hiding this comment

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

What you have here looks reasonable to me. What's going wrong?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will review more tonight. But off the top of my head.

T[: typing.TypeVar] = TypeVar("T")

the definition name is TypeVar so we from typing import TypeVar for example, but this doesnt help us. as we now have the annotation : typing.TypeVar.

Obviously not a great example because we should not type hint TypeVar (also a good place to not allow insert option`.

But for other types this would cause problems.

So its mostly an issue with the names.

Copy link
Member

Choose a reason for hiding this comment

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

Oh I see. You might need to use ImportAction::symbol_text, since the import might need to be fully qualified for correctness in some cases.

With that said, I think the specific issue you're hitting is that you're always requesting a from import statement via ImportRequest::import_from. If you want to respect an existing qualified import, then you'll want ImportRequest::import. So you'll probably need to detect which one you have and then use the right request. (Note though that it is only a request. So you still need to use ImportAction::symbol_text.)

I built the importer API with completions in mind. It might make sense to change ImportAction::symbol_text to return an Edit instead of a &str for your use case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, thanks. I'll leave this out for this PR i think, and have a go at it separately.

One thing i am thinking is that I don't know how many different formats of types we have like this. If it were just TypeVar and typing.TypeVar I could easily distinguish between them, but I don't know other ways this could show.

Copy link
Member

Choose a reason for hiding this comment

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

FWIW, T: TypeVar = TypeVar("T") is an invalid TypeVar declaration that ty will complain about, so we maybe shouldn't be offering a code action to add that to the user's code 😅

@Gankra
Copy link
Contributor

Gankra commented Nov 24, 2025

A rough heuristic for invalid syntax that will let you find most things: i believe (, ), <, >, ' are all invalid in type syntax (idk if ' is stricly invalid but anywhere we explicitly emit it we're doing type crimes).

@MatthewMckee4
Copy link
Contributor Author

Nice okay, do you think its worth it for us to scan over the label after getting the TypeDetails to check for invalid characters? Or should i just search for them in the display.rs file and manually set them to all be invalid

@Gankra
Copy link
Contributor

Gankra commented Nov 24, 2025

It's really tempting to just do the contains but I think it's probably best to mark up our code in display.rs yeah.

@MatthewMckee4
Copy link
Contributor Author

Is it ridiculous to try to make some property tests for this?

testing that after the edits have been accepted, that we have valid python syntax? And possibly no diagnostics on the file?

@Gankra
Copy link
Contributor

Gankra commented Nov 24, 2025

More tests is more better, I would have been lazy and just snapshot-tested like the rest of the IDE stuff 😅

@MatthewMckee4
Copy link
Contributor Author

I will try it then, see how it goes.

@Gankra
Copy link
Contributor

Gankra commented Nov 24, 2025

I will say we have a release tomorrow and I'd love to get some version of this PR in for that release (not a huge deal if not but I think this will really solidify inlay hints as Useful).

@MatthewMckee4 MatthewMckee4 marked this pull request as ready for review November 24, 2025 18:22
Copy link
Contributor

@Gankra Gankra left a comment

Choose a reason for hiding this comment

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

Rad! Just a few nits.

Comment on lines 448 to 451
fn dont_allow_edits(stmt_assign: &ruff_python_ast::StmtAssign) -> bool {
if stmt_assign.targets.len() > 1 {
return true;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh great call on this -- I might also call this is_valid_syntax, since that's ultimately the crux?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess its "if we add an annotation is it valid syntax".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But I'm not sure how to name that, "is_valid_syntax" doesn't seem exactly correct.

Copy link
Contributor

Choose a reason for hiding this comment

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

annotations_are_valid_syntax?

Comment on lines +5801 to +5805
class F:
@property
def whatever(self): ...

ab: property = F.whatever
Copy link
Contributor

Choose a reason for hiding this comment

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

(can be a followup) i have a sneaking suspicion this is a fake type

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah this didnt look great

Comment on lines 55 to 67
impl<'db> TypeDefinition<'db> {
pub fn definition<'a>(&'a self) -> Option<&'a Definition<'db>> {
match self {
Self::Module(_) => None,
Self::Class(definition)
| Self::Function(definition)
| Self::TypeVar(definition)
| Self::TypeAlias(definition)
| Self::SpecialForm(definition)
| Self::NewType(definition) => Some(definition),
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this code from the auto-import stuff?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah, will remove, thanks

@Gankra Gankra enabled auto-merge (squash) November 24, 2025 19:44
@Gankra Gankra merged commit 6f9265d into astral-sh:main Nov 24, 2025
40 checks passed
@Gankra
Copy link
Contributor

Gankra commented Nov 24, 2025

Hell yes hell yeah let's kick this things tires

dcreager added a commit that referenced this pull request Nov 24, 2025
…d-typevar

* origin/main: (30 commits)
  [ty] Double click to insert inlay hint (#21600)
  [ty] Switch the error code from `unresolved-attribute` to `possibly-missing-attribute` for submodules that may not be available (#21618)
  [ty] Substitute for `typing.Self` when checking protocol members (#21569)
  [ty] Don't suggest things that aren't subclasses of `BaseException` after `raise`
  [ty] Add hint about resolved Python version when a user attempts to import a member added on a newer version (#21615)
  Use release commit for actions/checkout (#21610)
  [ty] Add failing mdtest for known `Protocol` panic (#21594)
  [`parser`] Fix panic when parsing IPython escape command expressions (#21480)
  Fix cargo shear in CI (#21609)
  Update actions/checkout digest to c2d88d3 (#21601)
  Update dependency ruff to v0.14.6 (#21603)
  Update astral-sh/setup-uv action to v7.1.4 (#21602)
  Update Rust crate clap to v4.5.53 (#21604)
  Update taiki-e/install-action action to v2.62.56 (#21608)
  Update Rust crate hashbrown to v0.16.1 (#21605)
  Update Rust crate indexmap to v2.12.1 (#21606)
  Update Rust crate syn to v2.0.111 (#21607)
  [ty] Check method definitions on subclasses for Liskov violations (#21436)
  [ty] Fix panic for unclosed string literal in type annotation position (#21592)
  [ty] Fix rendering of unused suppression diagnostic (#21580)
  ...
carljm added a commit to mtshiba/ruff that referenced this pull request Nov 25, 2025
* main:
  [ty] Extend Liskov checks to also cover classmethods and staticmethods (astral-sh#21598)
  Dogfood ty on the `scripts` directory (astral-sh#21617)
  [ty] support generic aliases in `type[...]`, like `type[C[int]]` (astral-sh#21552)
  [ty] Retain the function-like-ness of `Callable` types when binding `self` (astral-sh#21614)
  [ty] Distinguish "unconstrained" from "constrained to any type" (astral-sh#21539)
  Disable ty workspace diagnostics for VSCode users (astral-sh#21620)
  [ty] Double click to insert inlay hint (astral-sh#21600)
  [ty] Switch the error code from `unresolved-attribute` to `possibly-missing-attribute` for submodules that may not be available (astral-sh#21618)
  [ty] Substitute for `typing.Self` when checking protocol members (astral-sh#21569)
  [ty] Don't suggest things that aren't subclasses of `BaseException` after `raise`
  [ty] Add hint about resolved Python version when a user attempts to import a member added on a newer version (astral-sh#21615)
@MatthewMckee4 MatthewMckee4 deleted the bake-inlay-hints branch December 27, 2025 00:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

server Related to the LSP server ty Multi-file analysis & type inference

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support double clicking to insert inlay hints in the editor ("bake inlay hints")

4 participants