|
| 1 | +use crate::goto::{find_goto_target, GotoTarget}; |
| 2 | +use crate::{Db, RangedValue}; |
| 3 | +use red_knot_python_semantic::types::Type; |
| 4 | +use red_knot_python_semantic::{HasType, SemanticModel}; |
| 5 | +use ruff_db::files::{File, FileRange}; |
| 6 | +use ruff_db::parsed::parsed_module; |
| 7 | +use ruff_text_size::{Ranged, TextSize}; |
| 8 | + |
| 9 | +pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option<RangedValue<Hover>> { |
| 10 | + let parsed = parsed_module(db.upcast(), file); |
| 11 | + let goto_target = find_goto_target(parsed, offset)?; |
| 12 | + |
| 13 | + let model = SemanticModel::new(db.upcast(), file); |
| 14 | + |
| 15 | + let ty = match goto_target { |
| 16 | + GotoTarget::Expression(expression) => expression.inferred_type(&model), |
| 17 | + GotoTarget::FunctionDef(function) => function.inferred_type(&model), |
| 18 | + GotoTarget::ClassDef(class) => class.inferred_type(&model), |
| 19 | + GotoTarget::Parameter(parameter) => parameter.inferred_type(&model), |
| 20 | + GotoTarget::Alias(alias) => alias.inferred_type(&model), |
| 21 | + GotoTarget::ExceptVariable(except) => except.inferred_type(&model), |
| 22 | + GotoTarget::KeywordArgument(argument) => { |
| 23 | + // TODO: Pyright resolves the declared type of the matching parameter. This seems more accurate |
| 24 | + // than using the inferred value. |
| 25 | + argument.value.inferred_type(&model) |
| 26 | + } |
| 27 | + |
| 28 | + // TODO: Better support for go to type definition in match pattern. |
| 29 | + // This may require improving type inference (e.g. it currently doesn't handle `...rest`) |
| 30 | + // but it also requires a new API to query the type because implementing `HasType` for `PatternMatchMapping` |
| 31 | + // is ambiguous. |
| 32 | + GotoTarget::PatternMatchRest(_) |
| 33 | + | GotoTarget::PatternKeywordArgument(_) |
| 34 | + | GotoTarget::PatternMatchStarName(_) |
| 35 | + | GotoTarget::PatternMatchAsName(_) => return None, |
| 36 | + |
| 37 | + // TODO: Resolve the module; The type inference already does all the work |
| 38 | + // but type isn't stored anywhere. We should either extract the logic |
| 39 | + // for resolving the module from a ImportFromStmt or store the type during semantic analysis |
| 40 | + GotoTarget::ImportedModule(_) => return None, |
| 41 | + |
| 42 | + // Targets without a type definition. |
| 43 | + GotoTarget::TypeParamTypeVarName(_) |
| 44 | + | GotoTarget::TypeParamParamSpecName(_) |
| 45 | + | GotoTarget::TypeParamTypeVarTupleName(_) => return None, |
| 46 | + GotoTarget::NonLocal { .. } | GotoTarget::Globals { .. } => return None, |
| 47 | + }; |
| 48 | + |
| 49 | + // TODO: Most LSPs show the symbol declaration: e.g. `class Foo: x: str` instead of just the name of the type |
| 50 | + // It also seems possible to return more than one markdown element and clients then display all of the |
| 51 | + // markdown blocks. |
| 52 | + tracing::debug!( |
| 53 | + "Inferred type of covering node is {}", |
| 54 | + ty.display(db.upcast()) |
| 55 | + ); |
| 56 | + |
| 57 | + Some(RangedValue { |
| 58 | + range: FileRange::new(file, goto_target.range()), |
| 59 | + value: Hover::Type(ty), |
| 60 | + }) |
| 61 | +} |
| 62 | + |
| 63 | +pub enum Hover<'db> { |
| 64 | + Type(Type<'db>), |
| 65 | +} |
| 66 | + |
| 67 | +impl Hover<'_> { |
| 68 | + pub fn to_string(&self, db: &dyn Db) -> String { |
| 69 | + match self { |
| 70 | + Hover::Type(ty) => ty.display(db.upcast()).to_string(), |
| 71 | + } |
| 72 | + } |
| 73 | +} |
0 commit comments