From 4119c43038c4542859ce5ecfca4635b8519f7b01 Mon Sep 17 00:00:00 2001 From: FroyaTheHen Date: Tue, 9 Dec 2025 10:43:18 +0800 Subject: [PATCH] extract and expose documentable_formatter fns commit-id:947b6186 SIERRA_UPDATE_NO_CHANGE_TAG=no change; tag missing after rebase --- .../src/documentable_formatter.rs | 248 ++++++++++-------- crates/cairo-lang-doc/src/location_links.rs | 13 +- crates/cairo-lang-doc/src/signature_errors.rs | 18 +- 3 files changed, 166 insertions(+), 113 deletions(-) diff --git a/crates/cairo-lang-doc/src/documentable_formatter.rs b/crates/cairo-lang-doc/src/documentable_formatter.rs index 6a0b8e53b58..93d8ced7982 100644 --- a/crates/cairo-lang-doc/src/documentable_formatter.rs +++ b/crates/cairo-lang-doc/src/documentable_formatter.rs @@ -139,6 +139,12 @@ impl<'db> HirFormatter<'db> { self.location_links.push(LocationLink { start, end, item_id }) } + /// Wraps `HirFormatter::write_str` call to satisfy the `SignatureError` return type. + fn hir_write(&mut self, s: &str) -> Result<(), SignatureError> { + self.write_str(s)?; + Ok(()) + } + /// Adds type's [`LocationLink`] to [`HirFormatter`] instance, formats and writes relevant /// signature slice. fn write_type( @@ -209,6 +215,24 @@ impl<'db> HirFormatter<'db> { self.buf = formatted_signature; self.location_links = moved_location_links; } + + /// Writes a chunk of text with [`LocationLink`]s into [`HirFormatter`] instance. + fn write_chunk( + &mut self, + text: &str, + location_links: Vec>, + ) -> Result<(), SignatureError> { + let offset = self.buf.len(); + self.buf.push_str(text); + for location_link in location_links { + self.add_location_link( + offset + location_link.start, + offset + location_link.end, + location_link.item_id, + ); + } + Ok(()) + } } impl<'db> HirDisplay<'db> for VariantId<'db> { @@ -296,7 +320,11 @@ impl<'db> HirDisplay<'db> for MemberId<'db> { &member_full_signature.full_path, ) } - .map_err(|_| SignatureError::FailedWritingType(member_full_signature.full_path.clone())) + .map_err(|_| { + SignatureError::FailedWritingSignatureFormatter( + member_full_signature.full_path.clone(), + ) + }) } else { Err(SignatureError::FailedRetrievingSemanticData(self.full_path(f.db))) } @@ -307,9 +335,10 @@ impl<'db> HirDisplay<'db> for StructId<'db> { fn hir_fmt(&self, f: &mut HirFormatter<'db>) -> Result<(), SignatureError> { let struct_full_signature = Self::retrieve_signature_data(f.db, *self)?; if let Some(attributes) = struct_full_signature.attributes { - write_struct_attributes_syntax(attributes, f).map_err(|_| { + let stx = get_struct_attributes_syntax(attributes, f.db).map_err(|_| { SignatureError::FailedWritingSignature(struct_full_signature.full_path.clone()) })?; + f.hir_write(&stx)?; } write!( f, @@ -321,9 +350,10 @@ impl<'db> HirDisplay<'db> for StructId<'db> { SignatureError::FailedWritingSignature(struct_full_signature.full_path.clone()) })?; if let Some(generic_params) = struct_full_signature.generic_params { - write_generic_params(generic_params, f).map_err(|_| { + let (stx, lls) = get_generic_params(generic_params, f.db).map_err(|_| { SignatureError::FailedWritingSignature(struct_full_signature.full_path.clone()) })?; + f.write_chunk(&stx, lls)?; } f.write_str(" {").map_err(|_| { SignatureError::FailedWritingSignature(struct_full_signature.full_path.clone()) @@ -379,20 +409,11 @@ impl<'db> HirDisplay<'db> for ConstantId<'db> { SignatureError::FailedWritingSignature(constant_full_signature.full_path.clone()) })?; if let Some(return_type) = constant_full_signature.return_type { - f.write_type(None, return_type, Some(" = "), &constant_full_signature.full_path) - .map_err(|_| { - SignatureError::FailedWritingSignature( - constant_full_signature.full_path.clone(), - ) - })?; + f.write_type(None, return_type, Some(" = "), &constant_full_signature.full_path)?; } if let Some(return_value_expr) = constant_full_signature.return_value_expr { match return_value_expr { - Expr::Literal(v) => write!(f, "{};", v.value,).map_err(|_| { - SignatureError::FailedWritingSignature( - constant_full_signature.full_path.clone(), - ) - }), + Expr::Literal(v) => write!(f, "{};", v.value), Expr::FunctionCall(_) => { let const_value_id = f.db.constant_const_value(*self).map_err(|_| { SignatureError::FailedRetrievingSemanticData( @@ -401,32 +422,24 @@ impl<'db> HirDisplay<'db> for ConstantId<'db> { })?; let constant_value = const_value_id.long(f.db); if let ConstValue::Int(value, _) = constant_value { - write_syntactic_evaluation(f, constant_full_signature.item_id).map_err( - |_| { - SignatureError::FailedWritingSignature( - constant_full_signature.full_path.clone(), - ) - }, - )?; - write!(f, " // = {value}") + let stx = get_syntactic_evaluation(constant_full_signature.item_id, f.db)?; + write!(f, "{stx} // = {value}") } else { - write_syntactic_evaluation(f, constant_full_signature.item_id) + let stx = get_syntactic_evaluation(constant_full_signature.item_id, f.db)?; + write!(f, "{stx};") } - .map_err(|_| { - SignatureError::FailedWritingSignature( - constant_full_signature.full_path.clone(), - ) - }) } - _ => write_syntactic_evaluation(f, constant_full_signature.item_id).map_err(|_| { - SignatureError::FailedWritingSignature( - constant_full_signature.full_path.clone(), - ) - }), + _ => write!( + f, + "{}", + get_syntactic_evaluation(constant_full_signature.item_id, f.db)? + ), } } else { - Err(SignatureError::FailedRetrievingSemanticData(self.full_path(f.db))) - } + write!(f, "{}", get_syntactic_evaluation(constant_full_signature.item_id, f.db)?) + }?; + f.format(); + Ok(()) } } @@ -444,9 +457,8 @@ impl<'db> HirDisplay<'db> for ImplConstantDefId<'db> { SignatureError::FailedWritingSignature(constant_full_signature.full_path.clone()) })?; } - write_syntactic_evaluation(f, constant_full_signature.item_id).map_err(|_| { - SignatureError::FailedWritingSignature(constant_full_signature.full_path) - })?; + let stx = get_syntactic_evaluation(constant_full_signature.item_id, f.db)?; + f.hir_write(&stx)?; f.format(); Ok(()) } @@ -480,14 +492,10 @@ impl<'db> HirDisplay<'db> for TraitId<'db> { "{}trait {}", get_syntactic_visibility(&trait_full_signature.visibility), trait_full_signature.name.long(f.db), - ) - .map_err(|_| { - SignatureError::FailedWritingSignature(trait_full_signature.full_path.clone()) - })?; + )?; if let Some(generic_params) = trait_full_signature.generic_params { - write_generic_params(generic_params, f).map_err(|_| { - SignatureError::FailedWritingSignature(trait_full_signature.full_path) - })? + let (stx, lls) = get_generic_params(generic_params, f.db)?; + f.write_chunk(&stx, lls)?; }; f.format(); Ok(()) @@ -561,9 +569,8 @@ impl<'db> HirDisplay<'db> for ImplAliasId<'db> { .map_err(|_| { SignatureError::FailedWritingSignature(impl_alias_full_signature.full_path.clone()) })?; - write_syntactic_evaluation(f, impl_alias_full_signature.item_id).map_err(|_| { - SignatureError::FailedWritingSignature(impl_alias_full_signature.full_path) - })?; + let stx = get_syntactic_evaluation(impl_alias_full_signature.item_id, f.db)?; + write!(f, "{}", stx)?; f.format(); Ok(()) } @@ -739,7 +746,7 @@ fn format_final_part(slice: &str) -> String { /// Takes a list of [`GenericParamId`]s and formats them into a string representation used for /// signature documentation. -fn format_resolver_generic_params<'db>( +pub fn format_resolver_generic_params<'db>( db: &'db dyn Database, params: Vec>, ) -> String { @@ -798,7 +805,7 @@ fn write_function_signature<'db>( f: &mut HirFormatter<'db>, documentable_signature: DocumentableItemSignatureData<'db>, syntactic_kind: String, -) -> Result<(), fmt::Error> { +) -> Result<(), SignatureError> { let resolver_generic_params = match documentable_signature.resolver_generic_params { Some(params) => format_resolver_generic_params(f.db, params), None => "".to_string(), @@ -811,11 +818,18 @@ fn write_function_signature<'db>( syntactic_kind, documentable_signature.name.long(f.db), resolver_generic_params, - )?; + ) + .map_err(|_| { + SignatureError::FailedWritingSignature(documentable_signature.full_path.clone()) + })?; if let Some(generic_args) = documentable_signature.generic_args { - write_generic_args(generic_args, f)?; + write_generic_args(generic_args, f).map_err(|_| { + SignatureError::FailedWritingSignature(documentable_signature.full_path.clone()) + })?; } - f.write_str("(")?; + f.write_str("(").map_err(|_| { + SignatureError::FailedWritingSignature(documentable_signature.full_path.clone()) + })?; if let Some(params) = documentable_signature.params { let mut count = params.len(); let mut postfix = String::from(", "); @@ -864,65 +878,85 @@ fn get_type_clause<'db>(syntax_node: SyntaxNode<'db>, db: &'db dyn Database) -> Some(String::from(MISSING)) } -/// Formats and writes [`GenericParam`]s data into [`HirFormatter`]'s buf. -fn write_generic_params<'db>( +/// Formats and returns [`GenericParam`]s data. +pub fn get_generic_params<'db>( generic_params: Vec>, - f: &mut HirFormatter<'db>, -) -> Result<(), fmt::Error> { + db: &'db dyn Database, +) -> Result<(String, Vec>), fmt::Error> { + let mut buff = String::new(); + let mut location_links: Vec> = Vec::new(); + if !generic_params.is_empty() { let mut count = generic_params.len(); - f.write_str("<")?; + buff.push('<'); + for param in generic_params { match param { GenericParam::Type(param_type) => { - let name = extract_and_format(param_type.id.format(f.db).long(f.db)); - write!(f, "{}{}", name, if count == 1 { "" } else { ", " })?; + let name = extract_and_format(param_type.id.format(db).long(db)); + buff.push_str(&format!("{}{}", name, if count == 1 { "" } else { ", " })); } GenericParam::Const(param_const) => { - let name = extract_and_format(param_const.id.format(f.db).long(f.db)); - write!(f, "const {}{}", name, if count == 1 { "" } else { ", " })?; + let name = extract_and_format(param_const.id.format(db).long(db)); + buff.push_str(&format!("const {}{}", name, if count == 1 { "" } else { ", " })); } GenericParam::Impl(param_impl) => { - let name = extract_and_format(param_impl.id.format(f.db).long(f.db)); + let name = extract_and_format(param_impl.id.format(db).long(db)); match param_impl.concrete_trait { Ok(concrete_trait) => { let documentable_id = DocumentableItemId::from(LookupItemId::ModuleItem( - ModuleItemId::Trait(concrete_trait.trait_id(f.db)), + ModuleItemId::Trait(concrete_trait.trait_id(db)), )); if name.starts_with("+") { - f.write_link(name, Some(documentable_id))?; + location_links.push(LocationLink::new( + buff.len(), + buff.len() + name.len(), + documentable_id, + 0, + )); + buff.push_str(&name); } else { - write!(f, "impl {name}: ")?; - let concrete_trait_name = concrete_trait.name(f.db); + buff.push_str(&format!("impl {name}: ")); + + let concrete_trait_name = concrete_trait.name(db); let concrete_trait_generic_args_formatted = concrete_trait - .generic_args(f.db) + .generic_args(db) .iter() - .map(|arg| extract_and_format(&arg.format(f.db))) + .map(|arg| extract_and_format(&arg.format(db))) .collect::>() .join(", "); - f.write_link( - concrete_trait_name.to_string(f.db), - Some(documentable_id), - )?; + + location_links.push(LocationLink::new( + buff.len(), + buff.len() + concrete_trait_name.to_string(db).len(), + documentable_id, + 0, + )); + buff.push_str(&concrete_trait_name.to_string(db)); + if !concrete_trait_generic_args_formatted.is_empty() { - write!(f, "<{concrete_trait_generic_args_formatted}>")?; + let f = format!("<{concrete_trait_generic_args_formatted}>"); + buff.push_str(&f); } } } Err(_) => { - write!(f, "{}{}", name, if count == 1 { "" } else { ", " })?; + buff.push_str(&format!( + "{}{}", + name, + if count == 1 { "" } else { ", " } + )); } } } - GenericParam::NegImpl(_) => f.write_str(MISSING)?, + GenericParam::NegImpl(_) => buff.push_str(MISSING), }; count -= 1; } - f.write_str(">") - } else { - Ok(()) + buff.push('>'); } + Ok((buff, location_links)) } /// Formats the syntax of generic arguments and writes it into [`HirFormatter`]. @@ -943,43 +977,44 @@ fn write_generic_args<'db>( Ok(()) } -/// Formats syntax of struct attributes and writes it into [`HirFormatter`]. -fn write_struct_attributes_syntax<'db>( +/// Formats and returns syntax of struct attributes. +pub fn get_struct_attributes_syntax<'db>( attributes: Vec>, - f: &mut HirFormatter<'db>, -) -> Result<(), fmt::Error> { + db: &'db dyn Database, +) -> Result { + let mut buff = String::new(); for attribute in attributes { - let syntax_node = attribute.stable_ptr.lookup(f.db).as_syntax_node(); - for child in syntax_node.get_children(f.db).iter() { - let to_text = child.get_text_without_all_comment_trivia(f.db); + let syntax_node = attribute.stable_ptr.lookup(db).as_syntax_node(); + for child in syntax_node.get_children(db).iter() { + let to_text = child.get_text_without_all_comment_trivia(db); let cleaned_text = to_text.replace("\n", ""); - f.write_str(&cleaned_text)?; + buff.push_str(&cleaned_text); } - f.write_str("\n")?; + buff.push('\n'); } - Ok(()) + Ok(buff) } -/// Formats syntax of documentable item and writes it into [`HirFormatter`]. -fn write_syntactic_evaluation<'db>( - f: &mut HirFormatter<'db>, +/// Formats and returns syntax of documentable item. +pub fn get_syntactic_evaluation<'db>( item_id: DocumentableItemId<'db>, -) -> Result<(), fmt::Error> { - if let Some(stable_location) = item_id.stable_location(f.db) { - let syntax_node = stable_location.syntax_node(f.db); - if matches!(&syntax_node.green_node(f.db).details, green::GreenNodeDetails::Node { .. }) { + db: &'db dyn Database, +) -> Result { + let mut buff = String::new(); + + if let Some(stable_location) = item_id.stable_location(db) { + let syntax_node = stable_location.syntax_node(db); + if matches!(&syntax_node.green_node(db).details, green::GreenNodeDetails::Node { .. }) { let mut is_after_evaluation_value = false; - for child in syntax_node.get_children(f.db).iter() { - let kind = child.kind(f.db); + for child in syntax_node.get_children(db).iter() { + let kind = child.kind(db); if !matches!(kind, SyntaxKind::Trivia) { if matches!(kind, SyntaxKind::TerminalSemicolon) { - f.buf.write_str(";")?; - return Ok(()); + buff.push(';'); + return Ok(buff); } if is_after_evaluation_value { - f.buf.write_str(&SyntaxNode::get_text_without_all_comment_trivia( - child, f.db, - ))?; + buff.push_str(&SyntaxNode::get_text_without_all_comment_trivia(child, db)); }; if matches!(kind, SyntaxKind::TerminalEq) { is_after_evaluation_value = true; @@ -987,7 +1022,7 @@ fn write_syntactic_evaluation<'db>( } } }; - Ok(()) + Ok(buff) } else { Err(fmt::Error) } @@ -1009,7 +1044,8 @@ fn write_type_signature<'db>( documentable_signature.name.long(f.db) )?; if let Some(generic_params) = documentable_signature.generic_params { - write_generic_params(generic_params, f)?; + let (stx, lls) = get_generic_params(generic_params, f.db)?; + f.write_chunk(&stx, lls).map_err(|_| fmt::Error)?; } if let Some(return_type) = documentable_signature.return_type { write!(f, " = ")?; @@ -1042,7 +1078,7 @@ fn resolve_generic_item<'db>( } /// Returns relevant [`DocumentableItemId`] from [`GenericModuleItemId`]. -fn resolve_generic_module_item<'db>( +pub fn resolve_generic_module_item<'db>( generic_module_item_id: GenericModuleItemId<'db>, ) -> DocumentableItemId<'db> { match generic_module_item_id { diff --git a/crates/cairo-lang-doc/src/location_links.rs b/crates/cairo-lang-doc/src/location_links.rs index c8cc295b548..11d5281a337 100644 --- a/crates/cairo-lang-doc/src/location_links.rs +++ b/crates/cairo-lang-doc/src/location_links.rs @@ -23,6 +23,13 @@ pub struct LocationLink<'db> { pub item_id: DocumentableItemId<'db>, } +impl<'db> LocationLink<'db> { + /// Creates a new instance of `LocationLink` moving `start` and `end` by given `offset`. + pub fn new(start: usize, end: usize, item_id: DocumentableItemId<'db>, offset: usize) -> Self { + Self { start: (start + offset), end: (end + offset), item_id } + } +} + /// Collects all [`cairo_lang_syntax::node::green::GreenNode`]s for a [`SyntaxNode`], /// returns a vector of their [`SyntaxKind`] and text. fn collect_green_nodes<'db>( @@ -102,10 +109,10 @@ fn get_offsets( } /// Adjusts [`LocationLink`]s based on differences created in signature syntax formatting. -fn move_location_links<'db>( - mut location_links: Vec>, +pub fn move_location_links( + mut location_links: Vec>, offset_vector: Vec<(usize, i32)>, -) -> Vec> { +) -> Vec> { for link in &mut location_links { let mut new_start = link.start as i32; let mut new_end = link.end as i32; diff --git a/crates/cairo-lang-doc/src/signature_errors.rs b/crates/cairo-lang-doc/src/signature_errors.rs index 795242079c6..b871566e839 100644 --- a/crates/cairo-lang-doc/src/signature_errors.rs +++ b/crates/cairo-lang-doc/src/signature_errors.rs @@ -4,7 +4,7 @@ use std::fmt; pub enum SignatureError { FailedRetrievingSemanticData(String), FailedWritingSignature(String), - FailedWritingType(String), + FailedWritingSignatureFormatter(String), } impl fmt::Display for SignatureError { @@ -16,11 +16,21 @@ impl fmt::Display for SignatureError { SignatureError::FailedWritingSignature(full_path) => { write!(f, "Failed writing signature for {full_path:?}.") } - SignatureError::FailedWritingType(full_path) => { - write!(f, "Failed writing a type for {full_path:?}.") + SignatureError::FailedWritingSignatureFormatter(e) => { + write!(f, "Failed writing signature formatter. {e:?}") } } } } -impl std::error::Error for SignatureError {} +impl From for SignatureError { + fn from(e: fmt::Error) -> Self { + SignatureError::FailedWritingSignatureFormatter(e.to_string()) + } +} + +impl From for fmt::Error { + fn from(_: SignatureError) -> Self { + fmt::Error + } +}