diff --git a/crates/cairo-lang-doc/src/documentable_formatter.rs b/crates/cairo-lang-doc/src/documentable_formatter.rs index 7babb995394..ce843eec162 100644 --- a/crates/cairo-lang-doc/src/documentable_formatter.rs +++ b/crates/cairo-lang-doc/src/documentable_formatter.rs @@ -2,37 +2,34 @@ use std::fmt; use std::fmt::Write; use std::option::Option; -use cairo_lang_defs::ids::TraitItemId::Function; use cairo_lang_defs::ids::{ - ConstantId, EnumId, ExternFunctionId, ExternTypeId, FreeFunctionId, GenericImplItemId, - GenericItemId, GenericKind, GenericModuleItemId, GenericParamId, GenericTraitItemId, - ImplAliasId, ImplConstantDefId, ImplDefId, ImplFunctionId, ImplItemId, ImplTypeDefId, - LanguageElementId, LookupItemId, MacroDeclarationId, MemberId, ModuleId, ModuleItemId, - ModuleTypeAliasId, NamedLanguageElementId, StructId, TopLevelLanguageElementId, - TraitConstantId, TraitFunctionId, TraitId, TraitItemId, TraitTypeId, VariantId, + ConstantId, EnumId, ExternFunctionId, ExternTypeId, FreeFunctionId, ImplAliasId, + ImplConstantDefId, ImplDefId, ImplFunctionId, ImplItemId, ImplTypeDefId, LanguageElementId, + LookupItemId, MacroDeclarationId, MemberId, ModuleItemId, ModuleTypeAliasId, + NamedLanguageElementId, StructId, TopLevelLanguageElementId, TraitConstantId, TraitFunctionId, + TraitId, TraitItemId, TraitTypeId, VariantId, }; use cairo_lang_filesystem::ids::Tracked; -use cairo_lang_semantic::expr::inference::InferenceId; use cairo_lang_semantic::items::constant::{ConstValue, ConstantSemantic}; use cairo_lang_semantic::items::enm::EnumSemantic; use cairo_lang_semantic::items::extern_function::ExternFunctionSemantic; -use cairo_lang_semantic::items::functions::GenericFunctionId; -use cairo_lang_semantic::items::generics::{GenericArgumentId, GenericParamSemantic}; +use cairo_lang_semantic::items::generics::GenericArgumentId; use cairo_lang_semantic::items::imp::ImplSemantic; use cairo_lang_semantic::items::macro_declaration::MacroDeclarationSemantic; use cairo_lang_semantic::items::modifiers::get_relevant_modifier; -use cairo_lang_semantic::items::us::UseSemantic; -use cairo_lang_semantic::items::visibility::Visibility; use cairo_lang_semantic::types::TypeId; -use cairo_lang_semantic::{ConcreteTypeId, Expr, GenericParam, TypeLongId}; -use cairo_lang_syntax::attribute::structured::Attribute; +use cairo_lang_semantic::{Expr, TypeLongId}; use cairo_lang_syntax::node::ast::WrappedMacro; use cairo_lang_syntax::node::kind::SyntaxKind; -use cairo_lang_syntax::node::{SyntaxNode, TypedStablePtr, TypedSyntaxNode, green}; +use cairo_lang_syntax::node::{SyntaxNode, TypedSyntaxNode}; use itertools::Itertools; use salsa::Database; use crate::documentable_item::DocumentableItemId; +use crate::helpers::{ + extract_and_format, format_resolver_generic_params, get_generic_params, + get_struct_attributes_syntax, get_syntactic_evaluation, get_syntactic_visibility, resolve_type, +}; use crate::location_links::{LocationLink, format_signature}; use crate::signature_data::{DocumentableItemSignatureData, SignatureDataRetriever}; use crate::signature_errors::SignatureError; @@ -40,7 +37,7 @@ use crate::signature_errors::SignatureError; /// Used for indenting children items of complex data type signature, e.g., struct members. const INDENT: &str = " "; /// Returned when item's signature could not be determined. -const MISSING: &str = ""; +pub(crate) const MISSING: &str = ""; /// Gets the signature of an item and a list of [`LocationLink`]s to enable mapping /// signature slices on documentable items. @@ -607,15 +604,6 @@ impl<'db> HirDisplay<'db> for MacroDeclarationId<'db> { } } -/// Formats the text of [`Visibility`] to a relevant string slice. -pub fn get_syntactic_visibility(semantic_visibility: &Visibility) -> &str { - match semantic_visibility { - Visibility::Public => "pub ", - Visibility::PublicInCrate => "pub(crate) ", - Visibility::Private => "", - } -} - /// Checks if the given paths come from the same root. fn is_the_same_root(path1: &str, path2: &str) -> bool { fn extract_root(input: &str) -> &str { @@ -624,102 +612,6 @@ fn is_the_same_root(path1: &str, path2: &str) -> bool { extract_root(path1) == extract_root(path2) } -/// Formats the full paths of complex types. For example, input "Result" results in output "Result". -fn extract_and_format(input: &str) -> String { - let delimiters = [',', '<', '>', '(', ')', '[', ']']; - let mut output = String::new(); - let mut slice_start = 0; - let mut in_slice = false; - - for (i, c) in input.char_indices() { - if delimiters.contains(&c) { - if in_slice { - let slice = &input[slice_start..i]; - output.push_str(&format_final_part(slice)); - in_slice = false; - } - output.push(c); - slice_start = i + 1; - } else { - in_slice = true; - } - } - if in_slice { - let slice = &input[slice_start..]; - output.push_str(&format_final_part(slice)); - } - output -} - -/// Formats a single type path. For example, input "core::felt252" results in output "felt252". -fn format_final_part(slice: &str) -> String { - let parts: Vec<&str> = slice.split("::").collect(); - let ensure_whitespace = - if let Some(first) = parts.first() { first.starts_with(" ") } else { false }; - let result = { - match parts[..] { - [.., before_last, ""] => before_last.to_string(), - [.., last] => last.to_string(), - _ => slice.to_string(), - } - }; - if ensure_whitespace && !result.starts_with(' ') { format!(" {result}") } else { result } -} - -/// Takes a list of [`GenericParamId`]s and formats them into a string representation used for -/// signature documentation. -pub fn format_resolver_generic_params<'db>( - db: &'db dyn Database, - params: Vec>, -) -> String { - if !params.is_empty() { - format!( - "<{}>", - params - .iter() - .map(|param| { - if matches!(param.kind(db), GenericKind::Impl) { - let param_formatted = param.format(db); - if param_formatted.long(db).starts_with("+") { - param_formatted.long(db).to_string() - } else { - match db.generic_param_semantic(*param) { - Ok(generic_param) => match generic_param { - GenericParam::Impl(generic_param_impl) => { - match generic_param_impl.concrete_trait { - Ok(concrete_trait) => { - format!( - "impl {}: {}<{}>", - param_formatted.long(db), - concrete_trait.name(db).long(db), - concrete_trait - .generic_args(db) - .iter() - .map(|arg| arg.format(db)) - .collect::>() - .join(", "), - ) - } - Err(_) => param_formatted.long(db).to_string(), - } - } - _ => param_formatted.long(db).to_string(), - }, - Err(_) => param_formatted.long(db).to_string(), - } - } - } else { - param.format(db).long(db).to_string() - } - }) - .join(", ") - ) - } else { - "".to_string() - } -} - /// A utility function for formatting documentable function data. Use with /// [`DocumentableItemSignatureData`] argument created for [`FreeFunctionId`], [`TraitFunctionId`], /// [`ImplFunctionId`] or [`ExternFunctionId`]. As those are the items for which a @@ -794,87 +686,6 @@ fn get_type_clause<'db>(syntax_node: SyntaxNode<'db>, db: &'db dyn Database) -> Some(String::from(MISSING)) } -/// Formats and returns [`GenericParam`]s data. -pub fn get_generic_params<'db>( - generic_params: Vec>, - 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(); - buff.push('<'); - - for param in generic_params { - match param { - GenericParam::Type(param_type) => { - 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(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(db).long(db)); - match param_impl.concrete_trait { - Ok(concrete_trait) => { - let documentable_id = - DocumentableItemId::from(LookupItemId::ModuleItem( - ModuleItemId::Trait(concrete_trait.trait_id(db)), - )); - if name.starts_with("+") { - location_links.push(LocationLink::new( - buff.len(), - buff.len() + name.len(), - documentable_id, - 0, - )); - buff.push_str(&name); - } else { - buff.push_str(&format!("impl {name}: ")); - - let concrete_trait_name = concrete_trait.name(db); - let concrete_trait_generic_args_formatted = concrete_trait - .generic_args(db) - .iter() - .map(|arg| extract_and_format(&arg.format(db))) - .collect::>() - .join(", "); - - 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() { - let f = format!("<{concrete_trait_generic_args_formatted}>"); - buff.push_str(&f); - } - } - } - Err(_) => { - buff.push_str(&format!( - "{}{}", - name, - if count == 1 { "" } else { ", " } - )); - } - } - } - GenericParam::NegImpl(_) => buff.push_str(MISSING), - }; - count -= 1; - } - buff.push('>'); - } - Ok((buff, location_links)) -} - /// Formats the syntax of generic arguments and writes it into [`HirFormatter`]. fn write_generic_args<'db>( generic_args: Vec>, @@ -893,57 +704,6 @@ fn write_generic_args<'db>( Ok(()) } -/// Formats and returns syntax of struct attributes. -pub fn get_struct_attributes_syntax<'db>( - attributes: Vec>, - db: &'db dyn Database, -) -> Result { - let mut buff = String::new(); - for attribute in attributes { - 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", ""); - buff.push_str(&cleaned_text); - } - buff.push('\n'); - } - Ok(buff) -} - -/// Formats and returns syntax of documentable item. -pub fn get_syntactic_evaluation<'db>( - item_id: DocumentableItemId<'db>, - 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(db).iter() { - let kind = child.kind(db); - if !matches!(kind, SyntaxKind::Trivia) { - if matches!(kind, SyntaxKind::TerminalSemicolon) { - buff.push(';'); - return Ok(buff); - } - if is_after_evaluation_value { - buff.push_str(&SyntaxNode::get_text_without_all_comment_trivia(child, db)); - }; - if matches!(kind, SyntaxKind::TerminalEq) { - is_after_evaluation_value = true; - } - } - } - }; - Ok(buff) - } else { - Err(fmt::Error) - } -} - /// A utility function used for formatting documentable types data. Use with /// [`DocumentableItemSignatureData`] argument created for [`ModuleTypeAliasId`], [`TraitTypeId`], /// [`ImplTypeDefId`] or [`ExternTypeId`]. Because of the same signature structure. @@ -971,70 +731,7 @@ fn write_type_signature<'db>( Ok(()) } -/// Returns relevant [`DocumentableItemId`] from [`GenericItemId`] if one can be retrieved. -fn resolve_generic_item<'db>( - generic_item_id: GenericItemId<'db>, - db: &'db dyn Database, -) -> Option> { - match generic_item_id { - GenericItemId::ModuleItem(module_item_id) => { - Some(resolve_generic_module_item(module_item_id)) - } - GenericItemId::TraitItem(generic_trait_item_id) => match generic_trait_item_id { - GenericTraitItemId::Type(trait_type_id) => Some(DocumentableItemId::from( - LookupItemId::ModuleItem(ModuleItemId::Trait(trait_type_id.trait_id(db))), - )), - }, - GenericItemId::ImplItem(generic_impl_item_id) => match generic_impl_item_id { - GenericImplItemId::Type(impl_type_def_id) => Some(DocumentableItemId::from( - LookupItemId::ModuleItem(ModuleItemId::Impl(impl_type_def_id.impl_def_id(db))), - )), - }, - } -} - -/// Returns relevant [`DocumentableItemId`] from [`GenericModuleItemId`]. -pub fn resolve_generic_module_item<'db>( - generic_module_item_id: GenericModuleItemId<'db>, -) -> DocumentableItemId<'db> { - match generic_module_item_id { - GenericModuleItemId::FreeFunc(id) => { - DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::FreeFunction(id))) - } - GenericModuleItemId::ExternFunc(id) => { - DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::ExternFunction(id))) - } - GenericModuleItemId::TraitFunc(id) => { - DocumentableItemId::from(LookupItemId::TraitItem(TraitItemId::Function(id))) - } - GenericModuleItemId::ImplFunc(id) => { - DocumentableItemId::from(LookupItemId::ImplItem(ImplItemId::Function(id))) - } - GenericModuleItemId::Trait(id) => { - DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Trait(id))) - } - GenericModuleItemId::Impl(id) => { - DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Impl(id))) - } - GenericModuleItemId::Struct(id) => { - DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Struct(id))) - } - GenericModuleItemId::Enum(id) => { - DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Enum(id))) - } - GenericModuleItemId::ExternType(id) => { - DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::ExternType(id))) - } - GenericModuleItemId::TypeAlias(id) => { - DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::TypeAlias(id))) - } - GenericModuleItemId::ImplAlias(id) => { - DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::ImplAlias(id))) - } - } -} - -/// Returns relevant [`DocumentableItemId`] from [`GenericArgumentId`] if one can be retrieved. +/// Returns relevant [`DocumentableItemId`] for [`GenericArgumentId`] if one can be retrieved. fn resolve_generic_arg<'db>( generic_arg_id: GenericArgumentId<'db>, db: &'db dyn Database, @@ -1057,89 +754,3 @@ fn resolve_generic_arg<'db>( GenericArgumentId::NegImpl(_) => None, } } - -/// Returns relevant [`DocumentableItemId`] from [`TypeId`] if one can be retrieved. -fn resolve_type<'db>( - db: &'db dyn Database, - type_id: TypeId<'db>, -) -> Option> { - let intern = type_id.long(db); - match intern { - TypeLongId::Concrete(concrete_type_id) => match concrete_type_id { - ConcreteTypeId::Struct(struct_id) => Some(DocumentableItemId::from( - LookupItemId::ModuleItem(ModuleItemId::Struct(struct_id.struct_id(db))), - )), - ConcreteTypeId::Enum(enum_id) => Some(DocumentableItemId::from( - LookupItemId::ModuleItem(ModuleItemId::Enum(enum_id.enum_id(db))), - )), - ConcreteTypeId::Extern(extern_id) => Some(DocumentableItemId::from( - LookupItemId::ModuleItem(ModuleItemId::ExternType(extern_id.extern_type_id(db))), - )), - }, - TypeLongId::Tuple(_) => None, - TypeLongId::Snapshot(type_id) => resolve_type(db, *type_id), - TypeLongId::GenericParameter(generic_param_id) => { - let item = generic_param_id.generic_item(db); - resolve_generic_item(item, db) - } - TypeLongId::Var(type_var) => match type_var.inference_id { - InferenceId::LookupItemDeclaration(lookup_item_id) - | InferenceId::LookupItemGenerics(lookup_item_id) - | InferenceId::LookupItemDefinition(lookup_item_id) => { - Some(DocumentableItemId::from(lookup_item_id)) - } - InferenceId::ImplDefTrait(impl_def_id) => Some(DocumentableItemId::from( - LookupItemId::ModuleItem(ModuleItemId::Impl(impl_def_id)), - )), - InferenceId::ImplAliasImplDef(impl_alias_id) => Some(DocumentableItemId::from( - LookupItemId::ModuleItem(ModuleItemId::ImplAlias(impl_alias_id)), - )), - InferenceId::GenericParam(generic_param_id) => { - let item = generic_param_id.generic_item(db); - resolve_generic_item(item, db) - } - InferenceId::GenericImplParamTrait(generic_param_id) => { - let item = generic_param_id.generic_item(db); - resolve_generic_item(item, db) - } - InferenceId::GlobalUseStar(global_use_id) => { - match db.priv_global_use_imported_module(global_use_id) { - Ok(module_id) => match module_id { - ModuleId::CrateRoot(crate_id) => Some(DocumentableItemId::from(crate_id)), - ModuleId::Submodule(submodule_id) => Some(DocumentableItemId::from( - LookupItemId::ModuleItem(ModuleItemId::Submodule(submodule_id)), - )), - ModuleId::MacroCall { .. } => None, - }, - Err(_) => None, - } - } - InferenceId::MacroCall(_) => None, - InferenceId::Canonical => None, - InferenceId::NoContext => None, - }, - TypeLongId::Coupon(function_id) => { - let concrete_function = function_id.get_concrete(db); - match concrete_function.generic_function { - GenericFunctionId::Free(function_id) => Some(DocumentableItemId::from( - LookupItemId::ModuleItem(ModuleItemId::FreeFunction(function_id)), - )), - GenericFunctionId::Extern(function_id) => Some(DocumentableItemId::from( - LookupItemId::ModuleItem(ModuleItemId::ExternFunction(function_id)), - )), - GenericFunctionId::Impl(function_id) => Some(DocumentableItemId::from( - LookupItemId::TraitItem(Function(function_id.function)), - )), - } - } - TypeLongId::FixedSizeArray { type_id, size: _ } => resolve_type(db, *type_id), - TypeLongId::ImplType(impl_type_id) => match impl_type_id.impl_id().concrete_trait(db) { - Ok(concrete_trait_id) => Some(DocumentableItemId::from(LookupItemId::ModuleItem( - ModuleItemId::Trait(concrete_trait_id.trait_id(db)), - ))), - Err(_) => None, - }, - TypeLongId::Closure(closure_type_id) => resolve_type(db, closure_type_id.ret_ty), - TypeLongId::Missing(_) => None, - } -} diff --git a/crates/cairo-lang-doc/src/helpers.rs b/crates/cairo-lang-doc/src/helpers.rs new file mode 100644 index 00000000000..8b374b62e64 --- /dev/null +++ b/crates/cairo-lang-doc/src/helpers.rs @@ -0,0 +1,407 @@ +use std::fmt; + +use cairo_lang_defs::ids::TraitItemId::Function; +use cairo_lang_defs::ids::{ + GenericImplItemId, GenericItemId, GenericKind, GenericModuleItemId, GenericParamId, + GenericTraitItemId, ImplItemId, LookupItemId, ModuleId, ModuleItemId, TraitItemId, +}; +use cairo_lang_semantic::expr::inference::InferenceId; +use cairo_lang_semantic::items::functions::GenericFunctionId; +use cairo_lang_semantic::items::generics::GenericParamSemantic; +use cairo_lang_semantic::items::us::UseSemantic; +use cairo_lang_semantic::items::visibility::Visibility; +use cairo_lang_semantic::{ConcreteTypeId, GenericParam, TypeId, TypeLongId}; +use cairo_lang_syntax::attribute::structured::Attribute; +use cairo_lang_syntax::node::kind::SyntaxKind; +use cairo_lang_syntax::node::{SyntaxNode, TypedStablePtr, TypedSyntaxNode, green}; +use itertools::Itertools; +use salsa::Database; + +use crate::documentable_item::DocumentableItemId; +use crate::location_links::LocationLink; + +/// Formats and returns [`GenericParam`]s data. +pub fn get_generic_params<'db>( + generic_params: Vec>, + 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(); + buff.push('<'); + + for param in generic_params { + match param { + GenericParam::Type(param_type) => { + 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(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(db).long(db)); + match param_impl.concrete_trait { + Ok(concrete_trait) => { + let documentable_id = + DocumentableItemId::from(LookupItemId::ModuleItem( + ModuleItemId::Trait(concrete_trait.trait_id(db)), + )); + if name.starts_with("+") { + location_links.push(LocationLink::new( + buff.len(), + buff.len() + name.len(), + documentable_id, + 0, + )); + buff.push_str(&name); + } else { + buff.push_str(&format!("impl {name}: ")); + + let concrete_trait_name = concrete_trait.name(db); + let concrete_trait_generic_args_formatted = concrete_trait + .generic_args(db) + .iter() + .map(|arg| extract_and_format(&arg.format(db))) + .collect::>() + .join(", "); + + 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() { + let f = format!("<{concrete_trait_generic_args_formatted}>"); + buff.push_str(&f); + } + } + } + Err(_) => { + buff.push_str(&format!( + "{}{}", + name, + if count == 1 { "" } else { ", " } + )); + } + } + } + GenericParam::NegImpl(_) => buff.push_str(crate::documentable_formatter::MISSING), + }; + count -= 1; + } + buff.push('>'); + } + Ok((buff, location_links)) +} + +/// Formats and returns syntax of documentable item. +pub fn get_syntactic_evaluation<'db>( + item_id: DocumentableItemId<'db>, + 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(db).iter() { + let kind = child.kind(db); + if !matches!(kind, SyntaxKind::Trivia) { + if matches!(kind, SyntaxKind::TerminalSemicolon) { + buff.push(';'); + return Ok(buff); + } + if is_after_evaluation_value { + buff.push_str(&SyntaxNode::get_text_without_all_comment_trivia(child, db)); + }; + if matches!(kind, SyntaxKind::TerminalEq) { + is_after_evaluation_value = true; + } + } + } + }; + Ok(buff) + } else { + Err(fmt::Error) + } +} + +/// Formats the text of [`Visibility`] to a relevant string slice. +pub fn get_syntactic_visibility(semantic_visibility: &Visibility) -> &str { + match semantic_visibility { + Visibility::Public => "pub ", + Visibility::PublicInCrate => "pub(crate) ", + Visibility::Private => "", + } +} + +/// Formats the full paths of complex types. For example, input "Result" results in output "Result". +pub(crate) fn extract_and_format(input: &str) -> String { + let delimiters = [',', '<', '>', '(', ')', '[', ']']; + let mut output = String::new(); + let mut slice_start = 0; + let mut in_slice = false; + + for (i, c) in input.char_indices() { + if delimiters.contains(&c) { + if in_slice { + let slice = &input[slice_start..i]; + output.push_str(&format_final_part(slice)); + in_slice = false; + } + output.push(c); + slice_start = i + 1; + } else { + in_slice = true; + } + } + if in_slice { + let slice = &input[slice_start..]; + output.push_str(&format_final_part(slice)); + } + output +} + +/// Formats a single type path. For example, input "core::felt252" results in output "felt252". +fn format_final_part(slice: &str) -> String { + let parts: Vec<&str> = slice.split("::").collect(); + let ensure_whitespace = + if let Some(first) = parts.first() { first.starts_with(" ") } else { false }; + let result = { + match parts[..] { + [.., before_last, ""] => before_last.to_string(), + [.., last] => last.to_string(), + _ => slice.to_string(), + } + }; + if ensure_whitespace && !result.starts_with(' ') { format!(" {result}") } else { result } +} + +/// Takes a list of [`GenericParamId`]s and formats it into a string representation used for +/// signature documentation. +pub fn format_resolver_generic_params<'db>( + db: &'db dyn Database, + params: Vec>, +) -> String { + if !params.is_empty() { + format!( + "<{}>", + params + .iter() + .map(|param| { + if matches!(param.kind(db), GenericKind::Impl) { + let param_formatted = param.format(db); + if param_formatted.long(db).starts_with("+") { + param_formatted.long(db).to_string() + } else { + match db.generic_param_semantic(*param) { + Ok(generic_param) => match generic_param { + GenericParam::Impl(generic_param_impl) => { + match generic_param_impl.concrete_trait { + Ok(concrete_trait) => { + format!( + "impl {}: {}<{}>", + param_formatted.long(db), + concrete_trait.name(db).long(db), + concrete_trait + .generic_args(db) + .iter() + .map(|arg| arg.format(db)) + .collect::>() + .join(", "), + ) + } + Err(_) => param_formatted.long(db).to_string(), + } + } + _ => param_formatted.long(db).to_string(), + }, + Err(_) => param_formatted.long(db).to_string(), + } + } + } else { + param.format(db).long(db).to_string() + } + }) + .join(", ") + ) + } else { + "".to_string() + } +} + +/// Formats and returns syntax of struct attributes. +pub fn get_struct_attributes_syntax<'db>( + attributes: Vec>, + db: &'db dyn Database, +) -> Result { + let mut buff = String::new(); + for attribute in attributes { + 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", ""); + buff.push_str(&cleaned_text); + } + buff.push('\n'); + } + Ok(buff) +} + +/// Returns relevant [`DocumentableItemId`] for [`TypeId`] if one can be retrieved. +pub fn resolve_type<'db>( + db: &'db dyn Database, + type_id: TypeId<'db>, +) -> Option> { + let intern = type_id.long(db); + match intern { + TypeLongId::Concrete(concrete_type_id) => match concrete_type_id { + ConcreteTypeId::Struct(struct_id) => Some(DocumentableItemId::from( + LookupItemId::ModuleItem(ModuleItemId::Struct(struct_id.struct_id(db))), + )), + ConcreteTypeId::Enum(enum_id) => Some(DocumentableItemId::from( + LookupItemId::ModuleItem(ModuleItemId::Enum(enum_id.enum_id(db))), + )), + ConcreteTypeId::Extern(extern_id) => Some(DocumentableItemId::from( + LookupItemId::ModuleItem(ModuleItemId::ExternType(extern_id.extern_type_id(db))), + )), + }, + TypeLongId::Tuple(_) => None, + TypeLongId::Snapshot(type_id) => resolve_type(db, *type_id), + TypeLongId::GenericParameter(generic_param_id) => { + let item = generic_param_id.generic_item(db); + resolve_generic_item(item, db) + } + TypeLongId::Var(type_var) => match type_var.inference_id { + InferenceId::LookupItemDeclaration(lookup_item_id) + | InferenceId::LookupItemGenerics(lookup_item_id) + | InferenceId::LookupItemDefinition(lookup_item_id) => { + Some(DocumentableItemId::from(lookup_item_id)) + } + InferenceId::ImplDefTrait(impl_def_id) => Some(DocumentableItemId::from( + LookupItemId::ModuleItem(ModuleItemId::Impl(impl_def_id)), + )), + InferenceId::ImplAliasImplDef(impl_alias_id) => Some(DocumentableItemId::from( + LookupItemId::ModuleItem(ModuleItemId::ImplAlias(impl_alias_id)), + )), + InferenceId::GenericParam(generic_param_id) => { + let item = generic_param_id.generic_item(db); + resolve_generic_item(item, db) + } + InferenceId::GenericImplParamTrait(generic_param_id) => { + let item = generic_param_id.generic_item(db); + resolve_generic_item(item, db) + } + InferenceId::GlobalUseStar(global_use_id) => { + match db.priv_global_use_imported_module(global_use_id) { + Ok(module_id) => match module_id { + ModuleId::CrateRoot(crate_id) => Some(DocumentableItemId::from(crate_id)), + ModuleId::Submodule(submodule_id) => Some(DocumentableItemId::from( + LookupItemId::ModuleItem(ModuleItemId::Submodule(submodule_id)), + )), + ModuleId::MacroCall { .. } => None, + }, + Err(_) => None, + } + } + InferenceId::MacroCall(_) => None, + InferenceId::Canonical => None, + InferenceId::NoContext => None, + }, + TypeLongId::Coupon(function_id) => { + let concrete_function = function_id.get_concrete(db); + match concrete_function.generic_function { + GenericFunctionId::Free(function_id) => Some(DocumentableItemId::from( + LookupItemId::ModuleItem(ModuleItemId::FreeFunction(function_id)), + )), + GenericFunctionId::Extern(function_id) => Some(DocumentableItemId::from( + LookupItemId::ModuleItem(ModuleItemId::ExternFunction(function_id)), + )), + GenericFunctionId::Impl(function_id) => Some(DocumentableItemId::from( + LookupItemId::TraitItem(Function(function_id.function)), + )), + } + } + TypeLongId::FixedSizeArray { type_id, size: _ } => resolve_type(db, *type_id), + TypeLongId::ImplType(impl_type_id) => match impl_type_id.impl_id().concrete_trait(db) { + Ok(concrete_trait_id) => Some(DocumentableItemId::from(LookupItemId::ModuleItem( + ModuleItemId::Trait(concrete_trait_id.trait_id(db)), + ))), + Err(_) => None, + }, + TypeLongId::Closure(closure_type_id) => resolve_type(db, closure_type_id.ret_ty), + TypeLongId::Missing(_) => None, + } +} + +/// Returns relevant [`DocumentableItemId`] for [`GenericItemId`] if one can be retrieved. +fn resolve_generic_item<'db>( + generic_item_id: GenericItemId<'db>, + db: &'db dyn Database, +) -> Option> { + match generic_item_id { + GenericItemId::ModuleItem(module_item_id) => { + Some(resolve_generic_module_item(module_item_id)) + } + GenericItemId::TraitItem(generic_trait_item_id) => match generic_trait_item_id { + GenericTraitItemId::Type(trait_type_id) => Some(DocumentableItemId::from( + LookupItemId::ModuleItem(ModuleItemId::Trait(trait_type_id.trait_id(db))), + )), + }, + GenericItemId::ImplItem(generic_impl_item_id) => match generic_impl_item_id { + GenericImplItemId::Type(impl_type_def_id) => Some(DocumentableItemId::from( + LookupItemId::ModuleItem(ModuleItemId::Impl(impl_type_def_id.impl_def_id(db))), + )), + }, + } +} + +/// Returns relevant [`DocumentableItemId`] for [`GenericModuleItemId`]. +pub fn resolve_generic_module_item( + generic_module_item_id: GenericModuleItemId<'_>, +) -> DocumentableItemId<'_> { + match generic_module_item_id { + GenericModuleItemId::FreeFunc(id) => { + DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::FreeFunction(id))) + } + GenericModuleItemId::ExternFunc(id) => { + DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::ExternFunction(id))) + } + GenericModuleItemId::TraitFunc(id) => { + DocumentableItemId::from(LookupItemId::TraitItem(TraitItemId::Function(id))) + } + GenericModuleItemId::ImplFunc(id) => { + DocumentableItemId::from(LookupItemId::ImplItem(ImplItemId::Function(id))) + } + GenericModuleItemId::Trait(id) => { + DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Trait(id))) + } + GenericModuleItemId::Impl(id) => { + DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Impl(id))) + } + GenericModuleItemId::Struct(id) => { + DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Struct(id))) + } + GenericModuleItemId::Enum(id) => { + DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::Enum(id))) + } + GenericModuleItemId::ExternType(id) => { + DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::ExternType(id))) + } + GenericModuleItemId::TypeAlias(id) => { + DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::TypeAlias(id))) + } + GenericModuleItemId::ImplAlias(id) => { + DocumentableItemId::from(LookupItemId::ModuleItem(ModuleItemId::ImplAlias(id))) + } + } +} diff --git a/crates/cairo-lang-doc/src/lib.rs b/crates/cairo-lang-doc/src/lib.rs index 6e4561cfb88..2e95bd52bc7 100644 --- a/crates/cairo-lang-doc/src/lib.rs +++ b/crates/cairo-lang-doc/src/lib.rs @@ -1,6 +1,7 @@ pub mod db; pub mod documentable_formatter; pub mod documentable_item; +pub mod helpers; pub mod location_links; pub mod parser; pub mod signature_data;