mirror of https://github.com/astral-sh/ruff
Merge b291969813 into 0f373603eb
This commit is contained in:
commit
54f4c1ca55
|
|
@ -1,12 +1,12 @@
|
||||||
use crate::docstring::Docstring;
|
use crate::docstring::Docstring;
|
||||||
use crate::goto::{GotoTarget, find_goto_target};
|
use crate::goto::{GotoTarget, find_goto_target};
|
||||||
use crate::{Db, MarkupKind, RangedValue};
|
use crate::{Db, HasNavigationTargets, MarkupKind, NavigationTarget, RangedValue};
|
||||||
use ruff_db::files::{File, FileRange};
|
use ruff_db::files::{File, FileRange};
|
||||||
use ruff_db::parsed::parsed_module;
|
use ruff_db::parsed::parsed_module;
|
||||||
use ruff_text_size::{Ranged, TextSize};
|
use ruff_text_size::{Ranged, TextSize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
use ty_python_semantic::types::{KnownInstanceType, Type, TypeVarVariance};
|
use ty_python_semantic::types::{KnownInstanceType, Type, TypeDetail, TypeVarVariance};
|
||||||
use ty_python_semantic::{DisplaySettings, SemanticModel};
|
use ty_python_semantic::{DisplaySettings, SemanticModel};
|
||||||
|
|
||||||
pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option<RangedValue<Hover<'_>>> {
|
pub fn hover(db: &dyn Db, file: File, offset: TextSize) -> Option<RangedValue<Hover<'_>>> {
|
||||||
|
|
@ -59,13 +59,21 @@ pub struct Hover<'db> {
|
||||||
contents: Vec<HoverContent<'db>>,
|
contents: Vec<HoverContent<'db>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Linkify<'a> = &'a dyn Fn(&NavigationTarget) -> Option<String>;
|
||||||
|
|
||||||
impl<'db> Hover<'db> {
|
impl<'db> Hover<'db> {
|
||||||
/// Renders the hover to a string using the specified markup kind.
|
/// Renders the hover to a string using the specified markup kind.
|
||||||
pub const fn display<'a>(&'a self, db: &'db dyn Db, kind: MarkupKind) -> DisplayHover<'db, 'a> {
|
pub const fn display<'a>(
|
||||||
|
&'a self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
kind: MarkupKind,
|
||||||
|
linkify: Linkify<'a>,
|
||||||
|
) -> DisplayHover<'db, 'a> {
|
||||||
DisplayHover {
|
DisplayHover {
|
||||||
db,
|
db,
|
||||||
hover: self,
|
hover: self,
|
||||||
kind,
|
kind,
|
||||||
|
linkify,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,6 +104,7 @@ pub struct DisplayHover<'db, 'a> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
hover: &'a Hover<'db>,
|
hover: &'a Hover<'db>,
|
||||||
kind: MarkupKind,
|
kind: MarkupKind,
|
||||||
|
linkify: Linkify<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for DisplayHover<'_, '_> {
|
impl fmt::Display for DisplayHover<'_, '_> {
|
||||||
|
|
@ -106,7 +115,7 @@ impl fmt::Display for DisplayHover<'_, '_> {
|
||||||
self.kind.horizontal_line().fmt(f)?;
|
self.kind.horizontal_line().fmt(f)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
content.display(self.db, self.kind).fmt(f)?;
|
content.display(self.db, self.kind, self.linkify).fmt(f)?;
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,11 +131,17 @@ pub enum HoverContent<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> HoverContent<'db> {
|
impl<'db> HoverContent<'db> {
|
||||||
fn display(&self, db: &'db dyn Db, kind: MarkupKind) -> DisplayHoverContent<'_, 'db> {
|
fn display<'a>(
|
||||||
|
&'a self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
kind: MarkupKind,
|
||||||
|
linkify: Linkify<'a>,
|
||||||
|
) -> DisplayHoverContent<'a, 'db> {
|
||||||
DisplayHoverContent {
|
DisplayHoverContent {
|
||||||
db,
|
db,
|
||||||
content: self,
|
content: self,
|
||||||
kind,
|
kind,
|
||||||
|
linkify,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -135,6 +150,7 @@ pub(crate) struct DisplayHoverContent<'a, 'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
content: &'a HoverContent<'db>,
|
content: &'a HoverContent<'db>,
|
||||||
kind: MarkupKind,
|
kind: MarkupKind,
|
||||||
|
linkify: Linkify<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for DisplayHoverContent<'_, '_> {
|
impl fmt::Display for DisplayHoverContent<'_, '_> {
|
||||||
|
|
@ -151,21 +167,84 @@ impl fmt::Display for DisplayHoverContent<'_, '_> {
|
||||||
Some(TypeVarVariance::Bivariant) => " (bivariant)",
|
Some(TypeVarVariance::Bivariant) => " (bivariant)",
|
||||||
None => "",
|
None => "",
|
||||||
};
|
};
|
||||||
self.kind
|
|
||||||
.fenced_code_block(
|
if self.kind == MarkupKind::Markdown {
|
||||||
format!(
|
let details = ty.display(self.db).to_string_parts();
|
||||||
"{}{variance}",
|
|
||||||
ty.display_with(self.db, DisplaySettings::default().multiline())
|
// Ok so the idea here is that we potentially have a random soup of spans here,
|
||||||
),
|
// and each byte of the string can have at most one target associate with it.
|
||||||
"python",
|
// Thankfully, they were generally pushed in print order, with the inner smaller types
|
||||||
)
|
// appearing before the outer bigger ones.
|
||||||
.fmt(f)
|
//
|
||||||
|
// So we record where we are in the string, and every time we find a type, we
|
||||||
|
// check if it's further along in the string. If it is, great, we give it the
|
||||||
|
// span for its range, and then advance where we are.
|
||||||
|
let mut offset = 0;
|
||||||
|
for (target, detail) in details.targets.iter().zip(&details.details) {
|
||||||
|
match detail {
|
||||||
|
TypeDetail::Type(ty) => {
|
||||||
|
let start = target.start().to_usize();
|
||||||
|
let end = target.end().to_usize();
|
||||||
|
// If we skipped over some bytes, push them with no target
|
||||||
|
if start > offset {
|
||||||
|
write!(f, "{}", escape(&details.label[offset..start]))?;
|
||||||
|
}
|
||||||
|
// Ok, this is the first type that claimed these bytes, give it the target
|
||||||
|
if start >= offset {
|
||||||
|
if let Some(target) =
|
||||||
|
ty.navigation_targets(self.db).into_iter().next()
|
||||||
|
&& let Some(uri) = (self.linkify)(&target)
|
||||||
|
{
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"[{}]({})",
|
||||||
|
escape(&details.label[start..end]),
|
||||||
|
uri
|
||||||
|
)?;
|
||||||
|
} else {
|
||||||
|
write!(f, "{}", escape(&details.label[start..end]))?;
|
||||||
|
}
|
||||||
|
offset = end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TypeDetail::SignatureStart
|
||||||
|
| TypeDetail::SignatureEnd
|
||||||
|
| TypeDetail::Parameter(_) => {
|
||||||
|
// Don't care about these
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// "flush" the rest of the label without any target
|
||||||
|
if offset < details.label.len() {
|
||||||
|
write!(f, "{}", escape(&details.label[offset..details.label.len()]))?;
|
||||||
|
}
|
||||||
|
write!(f, "{}", escape(variance))
|
||||||
|
} else {
|
||||||
|
self.kind
|
||||||
|
.fenced_code_block(
|
||||||
|
format!(
|
||||||
|
"{}{variance}",
|
||||||
|
ty.display_with(self.db, DisplaySettings::default().multiline())
|
||||||
|
),
|
||||||
|
"python",
|
||||||
|
)
|
||||||
|
.fmt(f)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
HoverContent::Docstring(docstring) => docstring.render(self.kind).fmt(f),
|
HoverContent::Docstring(docstring) => docstring.render(self.kind).fmt(f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn escape(x: &str) -> String {
|
||||||
|
x.replace('[', "\\[")
|
||||||
|
.replace(']', "\\]")
|
||||||
|
.replace('(', "\\(")
|
||||||
|
.replace(')', "\\)")
|
||||||
|
.replace('`', "\\`")
|
||||||
|
.replace('#', "\\#")
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::tests::{CursorTest, cursor_test};
|
use crate::tests::{CursorTest, cursor_test};
|
||||||
|
|
@ -3670,9 +3749,9 @@ def function():
|
||||||
write!(
|
write!(
|
||||||
&mut buf,
|
&mut buf,
|
||||||
"{plaintext}{line}{markdown}{line}",
|
"{plaintext}{line}{markdown}{line}",
|
||||||
plaintext = hover.display(&self.db, MarkupKind::PlainText),
|
plaintext = hover.display(&self.db, MarkupKind::PlainText, &|_| None),
|
||||||
line = MarkupKind::PlainText.horizontal_line(),
|
line = MarkupKind::PlainText.horizontal_line(),
|
||||||
markdown = hover.display(&self.db, MarkupKind::Markdown),
|
markdown = hover.display(&self.db, MarkupKind::Markdown, &|_| None),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::document::{FileRangeExt, PositionExt};
|
use crate::document::{FileRangeExt, PositionExt, ToRangeExt};
|
||||||
use crate::server::api::traits::{
|
use crate::server::api::traits::{
|
||||||
BackgroundDocumentRequestHandler, RequestHandler, RetriableRequestHandler,
|
BackgroundDocumentRequestHandler, RequestHandler, RetriableRequestHandler,
|
||||||
};
|
};
|
||||||
|
|
@ -8,7 +8,7 @@ use crate::session::DocumentSnapshot;
|
||||||
use crate::session::client::Client;
|
use crate::session::client::Client;
|
||||||
use lsp_types::request::HoverRequest;
|
use lsp_types::request::HoverRequest;
|
||||||
use lsp_types::{HoverContents, HoverParams, MarkupContent, Url};
|
use lsp_types::{HoverContents, HoverParams, MarkupContent, Url};
|
||||||
use ty_ide::{MarkupKind, hover};
|
use ty_ide::{MarkupKind, NavigationTarget, hover};
|
||||||
use ty_project::ProjectDatabase;
|
use ty_project::ProjectDatabase;
|
||||||
|
|
||||||
pub(crate) struct HoverRequestHandler;
|
pub(crate) struct HoverRequestHandler;
|
||||||
|
|
@ -61,7 +61,23 @@ impl BackgroundDocumentRequestHandler for HoverRequestHandler {
|
||||||
(MarkupKind::PlainText, lsp_types::MarkupKind::PlainText)
|
(MarkupKind::PlainText, lsp_types::MarkupKind::PlainText)
|
||||||
};
|
};
|
||||||
|
|
||||||
let contents = range_info.display(db, markup_kind).to_string();
|
let to_lsp_link = |target: &NavigationTarget| -> Option<String> {
|
||||||
|
let file = target.file();
|
||||||
|
let location = target
|
||||||
|
.focus_range()
|
||||||
|
.to_lsp_range(db, file, snapshot.encoding())?
|
||||||
|
.to_location()?;
|
||||||
|
let uri = location.uri;
|
||||||
|
let line1 = location.range.start.line;
|
||||||
|
let char1 = location.range.start.character;
|
||||||
|
let line2 = location.range.end.line;
|
||||||
|
let char2 = location.range.end.character;
|
||||||
|
Some(format!("{uri}#L{line1}C{char1}-L{line2}C{char2}"))
|
||||||
|
};
|
||||||
|
|
||||||
|
let contents = range_info
|
||||||
|
.display(db, markup_kind, &to_lsp_link)
|
||||||
|
.to_string();
|
||||||
|
|
||||||
Ok(Some(lsp_types::Hover {
|
Ok(Some(lsp_types::Hover {
|
||||||
contents: HoverContents::Markup(MarkupContent {
|
contents: HoverContents::Markup(MarkupContent {
|
||||||
|
|
|
||||||
|
|
@ -399,7 +399,7 @@ impl Workspace {
|
||||||
|
|
||||||
Ok(Some(Hover {
|
Ok(Some(Hover {
|
||||||
markdown: range_info
|
markdown: range_info
|
||||||
.display(&self.db, MarkupKind::Markdown)
|
.display(&self.db, MarkupKind::Markdown, &|_| None)
|
||||||
.to_string(),
|
.to_string(),
|
||||||
range: source_range,
|
range: source_range,
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue