mirror of
https://github.com/FuelLabs/sway.git
synced 2025-09-13 22:06:23 +00:00
Represent module level doc comments in forc doc
(#4096)
## Description Adds module level docs & previews to `forc doc`. [Screencast from 2023-02-20 20-30-24.webm](https://user-images.githubusercontent.com/57543709/220232486-7ec0742a-59ef-41c9-a459-3c8c9b7d42c7.webm) ## Checklist - [x] I have linked to any relevant issues. Closes #4095 - [x] I have commented my code, particularly in hard-to-understand areas. - [x] I have updated the documentation where relevant (API docs, the reference, and the Sway book). - [x] I have added tests that prove my fix is effective or that my feature works. - [x] I have added (or requested a maintainer to add) the necessary `Breaking*` or `New Feature` labels where relevant. - [x] I have done my best to ensure that my PR adheres to [the Fuel Labs Code Review Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md). - [x] I have requested a review from the relevant team or maintainers.
This commit is contained in:
parent
5d2b10bd83
commit
d9d5cb15a2
7 changed files with 131 additions and 72 deletions
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
descriptor::Descriptor,
|
descriptor::Descriptor,
|
||||||
render::{split_at_markdown_header, DocLink, ItemBody, ItemHeader, Renderable},
|
render::{split_at_markdown_header, DocLink, DocStrings, ItemBody, ItemHeader, Renderable},
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use horrorshow::{box_html, RenderBox};
|
use horrorshow::{box_html, RenderBox};
|
||||||
|
@ -50,35 +50,7 @@ impl Document {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn preview_opt(&self) -> Option<String> {
|
fn preview_opt(&self) -> Option<String> {
|
||||||
const MAX_PREVIEW_CHARS: usize = 100;
|
create_preview(self.raw_attributes.clone())
|
||||||
const CLOSING_PARAGRAPH_TAG: &str = "</p>";
|
|
||||||
|
|
||||||
self.raw_attributes.as_ref().map(|description| {
|
|
||||||
let preview = split_at_markdown_header(description);
|
|
||||||
if preview.chars().count() > MAX_PREVIEW_CHARS
|
|
||||||
&& preview.contains(CLOSING_PARAGRAPH_TAG)
|
|
||||||
{
|
|
||||||
match preview.find(CLOSING_PARAGRAPH_TAG) {
|
|
||||||
Some(index) => {
|
|
||||||
// We add 1 here to get the index of the char after the closing tag.
|
|
||||||
// This ensures we retain the closing tag and don't break the html.
|
|
||||||
let (preview, _) =
|
|
||||||
preview.split_at(index + CLOSING_PARAGRAPH_TAG.len() + 1);
|
|
||||||
if preview.chars().count() > MAX_PREVIEW_CHARS && preview.contains('\n') {
|
|
||||||
match preview.find('\n') {
|
|
||||||
Some(index) => preview.split_at(index).0.to_string(),
|
|
||||||
None => unreachable!("Previous logic prevents this panic"),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
preview.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => unreachable!("Previous logic prevents this panic"),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
preview.to_string()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
/// Gather [Documentation] from the [TyProgram].
|
/// Gather [Documentation] from the [TyProgram].
|
||||||
pub(crate) fn from_ty_program(
|
pub(crate) fn from_ty_program(
|
||||||
|
@ -95,7 +67,7 @@ impl Document {
|
||||||
let desc = Descriptor::from_typed_decl(
|
let desc = Descriptor::from_typed_decl(
|
||||||
decl_engine,
|
decl_engine,
|
||||||
decl,
|
decl,
|
||||||
ModuleInfo::from_vec(vec![project_name.to_owned()]),
|
ModuleInfo::from_ty_module(vec![project_name.to_owned()], None),
|
||||||
document_private_items,
|
document_private_items,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -108,7 +80,10 @@ impl Document {
|
||||||
if !no_deps && !typed_program.root.submodules.is_empty() {
|
if !no_deps && !typed_program.root.submodules.is_empty() {
|
||||||
// this is the same process as before but for dependencies
|
// this is the same process as before but for dependencies
|
||||||
for (_, ref typed_submodule) in &typed_program.root.submodules {
|
for (_, ref typed_submodule) in &typed_program.root.submodules {
|
||||||
let module_prefix = ModuleInfo::from_vec(vec![project_name.to_owned()]);
|
let attributes = (!typed_submodule.module.attributes.is_empty())
|
||||||
|
.then(|| typed_submodule.module.attributes.to_html_string());
|
||||||
|
let module_prefix =
|
||||||
|
ModuleInfo::from_ty_module(vec![project_name.to_owned()], attributes);
|
||||||
Document::from_ty_submodule(
|
Document::from_ty_submodule(
|
||||||
decl_engine,
|
decl_engine,
|
||||||
typed_submodule,
|
typed_submodule,
|
||||||
|
@ -130,7 +105,7 @@ impl Document {
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut new_submodule_prefix = module_prefix.to_owned();
|
let mut new_submodule_prefix = module_prefix.to_owned();
|
||||||
new_submodule_prefix
|
new_submodule_prefix
|
||||||
.0
|
.module_prefixes
|
||||||
.push(typed_submodule.library_name.as_str().to_owned());
|
.push(typed_submodule.library_name.as_str().to_owned());
|
||||||
for ast_node in &typed_submodule.module.all_nodes {
|
for ast_node in &typed_submodule.module.all_nodes {
|
||||||
if let TyAstNodeContent::Declaration(ref decl) = ast_node.content {
|
if let TyAstNodeContent::Declaration(ref decl) = ast_node.content {
|
||||||
|
@ -173,13 +148,16 @@ impl Renderable for Document {
|
||||||
|
|
||||||
pub(crate) type ModulePrefix = String;
|
pub(crate) type ModulePrefix = String;
|
||||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
pub(crate) struct ModuleInfo(pub(crate) Vec<ModulePrefix>);
|
pub(crate) struct ModuleInfo {
|
||||||
|
pub(crate) module_prefixes: Vec<ModulePrefix>,
|
||||||
|
pub(crate) attributes: Option<String>,
|
||||||
|
}
|
||||||
impl ModuleInfo {
|
impl ModuleInfo {
|
||||||
/// The current module.
|
/// The current module.
|
||||||
///
|
///
|
||||||
/// Panics if there are no modules.
|
/// Panics if there are no modules.
|
||||||
pub(crate) fn location(&self) -> &str {
|
pub(crate) fn location(&self) -> &str {
|
||||||
self.0
|
self.module_prefixes
|
||||||
.last()
|
.last()
|
||||||
.expect("Expected Some module location, found None")
|
.expect("Expected Some module location, found None")
|
||||||
}
|
}
|
||||||
|
@ -187,7 +165,7 @@ impl ModuleInfo {
|
||||||
///
|
///
|
||||||
/// Panics if the project root is missing.
|
/// Panics if the project root is missing.
|
||||||
pub(crate) fn project_name(&self) -> &str {
|
pub(crate) fn project_name(&self) -> &str {
|
||||||
self.0
|
self.module_prefixes
|
||||||
.first()
|
.first()
|
||||||
.expect("Expected root module, project root missing")
|
.expect("Expected root module, project root missing")
|
||||||
}
|
}
|
||||||
|
@ -197,7 +175,7 @@ impl ModuleInfo {
|
||||||
pub(crate) fn parent(&self) -> Option<&String> {
|
pub(crate) fn parent(&self) -> Option<&String> {
|
||||||
match self.has_parent() {
|
match self.has_parent() {
|
||||||
true => {
|
true => {
|
||||||
let mut iter = self.0.iter();
|
let mut iter = self.module_prefixes.iter();
|
||||||
iter.next_back();
|
iter.next_back();
|
||||||
iter.next_back()
|
iter.next_back()
|
||||||
}
|
}
|
||||||
|
@ -226,7 +204,7 @@ impl ModuleInfo {
|
||||||
///
|
///
|
||||||
/// Example: `module::submodule`
|
/// Example: `module::submodule`
|
||||||
fn to_path_literal_prefix(&self, location: &str) -> String {
|
fn to_path_literal_prefix(&self, location: &str) -> String {
|
||||||
let mut iter = self.0.iter();
|
let mut iter = self.module_prefixes.iter();
|
||||||
for prefix in iter.by_ref() {
|
for prefix in iter.by_ref() {
|
||||||
if prefix == location {
|
if prefix == location {
|
||||||
break;
|
break;
|
||||||
|
@ -239,7 +217,7 @@ impl ModuleInfo {
|
||||||
///
|
///
|
||||||
/// This is only used for full path syntax, e.g `module/submodule/file_name.html`.
|
/// This is only used for full path syntax, e.g `module/submodule/file_name.html`.
|
||||||
pub(crate) fn to_file_path_string(&self, file_name: &str, location: &str) -> Result<String> {
|
pub(crate) fn to_file_path_string(&self, file_name: &str, location: &str) -> Result<String> {
|
||||||
let mut iter = self.0.iter();
|
let mut iter = self.module_prefixes.iter();
|
||||||
for prefix in iter.by_ref() {
|
for prefix in iter.by_ref() {
|
||||||
if prefix == location {
|
if prefix == location {
|
||||||
break;
|
break;
|
||||||
|
@ -265,12 +243,47 @@ impl ModuleInfo {
|
||||||
}
|
}
|
||||||
/// The depth of a module as `usize`.
|
/// The depth of a module as `usize`.
|
||||||
pub(crate) fn depth(&self) -> usize {
|
pub(crate) fn depth(&self) -> usize {
|
||||||
self.0.len()
|
self.module_prefixes.len()
|
||||||
}
|
}
|
||||||
/// Create a new [ModuleInfo] from a vec.
|
/// Create a new [ModuleInfo] from a vec.
|
||||||
pub(crate) fn from_vec(vec: Vec<String>) -> Self {
|
pub(crate) fn from_ty_module(module_prefixes: Vec<String>, attributes: Option<String>) -> Self {
|
||||||
Self(vec)
|
Self {
|
||||||
|
module_prefixes,
|
||||||
|
attributes,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
pub(crate) fn preview_opt(&self) -> Option<String> {
|
||||||
|
create_preview(self.attributes.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a docstring preview from raw html attributes.
|
||||||
|
///
|
||||||
|
/// Returns `None` if there are no attributes.
|
||||||
|
fn create_preview(raw_attributes: Option<String>) -> Option<String> {
|
||||||
|
const MAX_PREVIEW_CHARS: usize = 100;
|
||||||
|
const CLOSING_PARAGRAPH_TAG: &str = "</p>";
|
||||||
|
|
||||||
|
raw_attributes.as_ref().map(|description| {
|
||||||
|
let preview = split_at_markdown_header(description);
|
||||||
|
if preview.chars().count() > MAX_PREVIEW_CHARS && preview.contains(CLOSING_PARAGRAPH_TAG) {
|
||||||
|
let closing_tag_index = preview
|
||||||
|
.find(CLOSING_PARAGRAPH_TAG)
|
||||||
|
.expect("closing tag out of range");
|
||||||
|
// We add 1 here to get the index of the char after the closing tag.
|
||||||
|
// This ensures we retain the closing tag and don't break the html.
|
||||||
|
let (preview, _) =
|
||||||
|
preview.split_at(closing_tag_index + CLOSING_PARAGRAPH_TAG.len() + 1);
|
||||||
|
if preview.chars().count() > MAX_PREVIEW_CHARS && preview.contains('\n') {
|
||||||
|
let newline_index = preview.find('\n').expect("new line char out of range");
|
||||||
|
preview.split_at(newline_index).0.to_string()
|
||||||
|
} else {
|
||||||
|
preview.to_string()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
preview.to_string()
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -283,12 +296,12 @@ mod tests {
|
||||||
let module = String::from("module_name");
|
let module = String::from("module_name");
|
||||||
let mut module_vec = vec![project.clone(), module];
|
let mut module_vec = vec![project.clone(), module];
|
||||||
|
|
||||||
let module_info = ModuleInfo::from_vec(module_vec.clone());
|
let module_info = ModuleInfo::from_ty_module(module_vec.clone(), None);
|
||||||
let project_opt = module_info.parent();
|
let project_opt = module_info.parent();
|
||||||
assert_eq!(Some(&project), project_opt);
|
assert_eq!(Some(&project), project_opt);
|
||||||
|
|
||||||
module_vec.pop();
|
module_vec.pop();
|
||||||
let module_info = ModuleInfo::from_vec(module_vec);
|
let module_info = ModuleInfo::from_ty_module(module_vec, None);
|
||||||
let project_opt = module_info.parent();
|
let project_opt = module_info.parent();
|
||||||
assert_eq!(None, project_opt);
|
assert_eq!(None, project_opt);
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,18 +82,22 @@ pub fn main() -> Result<()> {
|
||||||
no_deps,
|
no_deps,
|
||||||
document_private_items,
|
document_private_items,
|
||||||
)?;
|
)?;
|
||||||
|
let root_attributes =
|
||||||
|
(!typed_program.root.attributes.is_empty()).then_some(typed_program.root.attributes);
|
||||||
|
let program_kind = typed_program.kind;
|
||||||
// render docs to HTML
|
// render docs to HTML
|
||||||
let forc_version = pkg_manifest
|
let forc_version = pkg_manifest
|
||||||
.project
|
.project
|
||||||
.forc_version
|
.forc_version
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|ver| format!("{}.{}.{}", ver.major, ver.minor, ver.patch));
|
.map(|ver| format!("{}.{}.{}", ver.major, ver.minor, ver.patch));
|
||||||
let rendered_docs = RenderedDocumentation::from(raw_docs, forc_version)?;
|
let rendered_docs =
|
||||||
|
RenderedDocumentation::from(raw_docs, root_attributes, program_kind, forc_version)?;
|
||||||
|
|
||||||
// write contents to outfile
|
// write contents to outfile
|
||||||
for doc in rendered_docs.0 {
|
for doc in rendered_docs.0 {
|
||||||
let mut doc_path = doc_path.clone();
|
let mut doc_path = doc_path.clone();
|
||||||
for prefix in doc.module_info.0 {
|
for prefix in doc.module_info.module_prefixes {
|
||||||
if &prefix != project_name {
|
if &prefix != project_name {
|
||||||
doc_path.push(prefix);
|
doc_path.push(prefix);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::collections::BTreeMap;
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use sway_core::language::ty::{
|
use sway_core::language::ty::{
|
||||||
TyDeclaration::{self, *},
|
TyDeclaration::{self, *},
|
||||||
TyEnumVariant, TyStorageField, TyStructField, TyTraitFn,
|
TyEnumVariant, TyProgramKind, TyStorageField, TyStructField, TyTraitFn,
|
||||||
};
|
};
|
||||||
use sway_core::transform::{AttributeKind, AttributesMap};
|
use sway_core::transform::{AttributeKind, AttributesMap};
|
||||||
use sway_lsp::utils::markdown::format_docs;
|
use sway_lsp::utils::markdown::format_docs;
|
||||||
|
@ -29,14 +29,22 @@ pub(crate) struct RenderedDocumentation(pub(crate) Vec<RenderedDocument>);
|
||||||
|
|
||||||
impl RenderedDocumentation {
|
impl RenderedDocumentation {
|
||||||
/// Top level HTML rendering for all [Documentation] of a program.
|
/// Top level HTML rendering for all [Documentation] of a program.
|
||||||
pub fn from(raw: Documentation, forc_version: Option<String>) -> Result<RenderedDocumentation> {
|
pub fn from(
|
||||||
|
raw: Documentation,
|
||||||
|
root_attributes: Option<AttributesMap>,
|
||||||
|
program_kind: TyProgramKind,
|
||||||
|
forc_version: Option<String>,
|
||||||
|
) -> Result<RenderedDocumentation> {
|
||||||
let mut rendered_docs: RenderedDocumentation = Default::default();
|
let mut rendered_docs: RenderedDocumentation = Default::default();
|
||||||
let root_module = match raw.first() {
|
let root_module = match raw.first() {
|
||||||
Some(doc) => ModuleInfo::from_vec(vec![doc.module_info.project_name().to_owned()]),
|
Some(doc) => ModuleInfo::from_ty_module(
|
||||||
|
vec![doc.module_info.project_name().to_owned()],
|
||||||
|
root_attributes.map(|attrs_map| attrs_map.to_html_string()),
|
||||||
|
),
|
||||||
None => panic!("Project does not contain a root module"),
|
None => panic!("Project does not contain a root module"),
|
||||||
};
|
};
|
||||||
let mut all_docs = DocLinks {
|
let mut all_docs = DocLinks {
|
||||||
style: DocStyle::AllDoc,
|
style: DocStyle::AllDoc(program_kind.as_title_str().to_string()),
|
||||||
links: Default::default(),
|
links: Default::default(),
|
||||||
};
|
};
|
||||||
let mut module_map: BTreeMap<ModulePrefix, BTreeMap<BlockTitle, Vec<DocLink>>> =
|
let mut module_map: BTreeMap<ModulePrefix, BTreeMap<BlockTitle, Vec<DocLink>>> =
|
||||||
|
@ -138,7 +146,7 @@ impl RenderedDocumentation {
|
||||||
name: location.clone(),
|
name: location.clone(),
|
||||||
module_info: doc.module_info.to_owned(),
|
module_info: doc.module_info.to_owned(),
|
||||||
html_filename: INDEX_FILENAME.to_owned(),
|
html_filename: INDEX_FILENAME.to_owned(),
|
||||||
preview_opt: None,
|
preview_opt: doc.module_info.preview_opt(),
|
||||||
};
|
};
|
||||||
match module_map.get_mut(parent_module) {
|
match module_map.get_mut(parent_module) {
|
||||||
Some(doc_links) => match doc_links.get_mut(&BlockTitle::Modules) {
|
Some(doc_links) => match doc_links.get_mut(&BlockTitle::Modules) {
|
||||||
|
@ -227,7 +235,7 @@ impl RenderedDocumentation {
|
||||||
version_opt: forc_version,
|
version_opt: forc_version,
|
||||||
module_info: root_module.clone(),
|
module_info: root_module.clone(),
|
||||||
module_docs: DocLinks {
|
module_docs: DocLinks {
|
||||||
style: DocStyle::ProjectIndex,
|
style: DocStyle::ProjectIndex(program_kind.as_title_str().to_string()),
|
||||||
links: doc_links.to_owned(),
|
links: doc_links.to_owned(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -485,7 +493,7 @@ impl ItemContext {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|field| DocLink {
|
.map(|field| DocLink {
|
||||||
name: field.name.as_str().to_string(),
|
name: field.name.as_str().to_string(),
|
||||||
module_info: ModuleInfo::from_vec(vec![]),
|
module_info: ModuleInfo::from_ty_module(vec![], None),
|
||||||
html_filename: format!(
|
html_filename: format!(
|
||||||
"{}structfield.{}",
|
"{}structfield.{}",
|
||||||
IDENTITY,
|
IDENTITY,
|
||||||
|
@ -501,7 +509,7 @@ impl ItemContext {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|field| DocLink {
|
.map(|field| DocLink {
|
||||||
name: field.name.as_str().to_string(),
|
name: field.name.as_str().to_string(),
|
||||||
module_info: ModuleInfo::from_vec(vec![]),
|
module_info: ModuleInfo::from_ty_module(vec![], None),
|
||||||
html_filename: format!(
|
html_filename: format!(
|
||||||
"{}storagefield.{}",
|
"{}storagefield.{}",
|
||||||
IDENTITY,
|
IDENTITY,
|
||||||
|
@ -517,7 +525,7 @@ impl ItemContext {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|variant| DocLink {
|
.map(|variant| DocLink {
|
||||||
name: variant.name.as_str().to_string(),
|
name: variant.name.as_str().to_string(),
|
||||||
module_info: ModuleInfo::from_vec(vec![]),
|
module_info: ModuleInfo::from_ty_module(vec![], None),
|
||||||
html_filename: format!("{}variant.{}", IDENTITY, variant.name.as_str()),
|
html_filename: format!("{}variant.{}", IDENTITY, variant.name.as_str()),
|
||||||
preview_opt: None,
|
preview_opt: None,
|
||||||
})
|
})
|
||||||
|
@ -529,7 +537,7 @@ impl ItemContext {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|method| DocLink {
|
.map(|method| DocLink {
|
||||||
name: method.name.as_str().to_string(),
|
name: method.name.as_str().to_string(),
|
||||||
module_info: ModuleInfo::from_vec(vec![]),
|
module_info: ModuleInfo::from_ty_module(vec![], None),
|
||||||
html_filename: format!(
|
html_filename: format!(
|
||||||
"{}structfield.{}",
|
"{}structfield.{}",
|
||||||
IDENTITY,
|
IDENTITY,
|
||||||
|
@ -749,7 +757,7 @@ struct DocLinks {
|
||||||
impl Renderable for DocLinks {
|
impl Renderable for DocLinks {
|
||||||
fn render(self) -> Result<Box<dyn RenderBox>> {
|
fn render(self) -> Result<Box<dyn RenderBox>> {
|
||||||
let doc_links = match self.style {
|
let doc_links = match self.style {
|
||||||
DocStyle::AllDoc => box_html! {
|
DocStyle::AllDoc(_) => box_html! {
|
||||||
@ for (title, list_items) in self.links {
|
@ for (title, list_items) in self.links {
|
||||||
@ if !list_items.is_empty() {
|
@ if !list_items.is_empty() {
|
||||||
h3(id=format!("{}", title.html_title_string())) { : title.as_str(); }
|
h3(id=format!("{}", title.html_title_string())) { : title.as_str(); }
|
||||||
|
@ -777,7 +785,7 @@ impl Renderable for DocLinks {
|
||||||
}
|
}
|
||||||
.into_string()
|
.into_string()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
DocStyle::ProjectIndex => box_html! {
|
DocStyle::ProjectIndex(_) => box_html! {
|
||||||
@ for (title, list_items) in self.links {
|
@ for (title, list_items) in self.links {
|
||||||
@ if !list_items.is_empty() {
|
@ if !list_items.is_empty() {
|
||||||
h3(id=format!("{}", title.html_title_string())) { : title.as_str(); }
|
h3(id=format!("{}", title.html_title_string())) { : title.as_str(); }
|
||||||
|
@ -914,7 +922,7 @@ impl SidebarNav for AllDocIndex {
|
||||||
fn sidebar(&self) -> Sidebar {
|
fn sidebar(&self) -> Sidebar {
|
||||||
Sidebar {
|
Sidebar {
|
||||||
version_opt: None,
|
version_opt: None,
|
||||||
style: DocStyle::AllDoc,
|
style: self.all_docs.style.clone(),
|
||||||
module_info: self.project_name.clone(),
|
module_info: self.project_name.clone(),
|
||||||
href_path: INDEX_FILENAME.to_owned(),
|
href_path: INDEX_FILENAME.to_owned(),
|
||||||
nav: self.all_docs.clone(),
|
nav: self.all_docs.clone(),
|
||||||
|
@ -989,7 +997,7 @@ pub(crate) struct ModuleIndex {
|
||||||
impl SidebarNav for ModuleIndex {
|
impl SidebarNav for ModuleIndex {
|
||||||
fn sidebar(&self) -> Sidebar {
|
fn sidebar(&self) -> Sidebar {
|
||||||
let style = match self.module_info.is_root_module() {
|
let style = match self.module_info.is_root_module() {
|
||||||
true => DocStyle::ProjectIndex,
|
true => self.module_docs.style.clone(),
|
||||||
false => DocStyle::ModuleIndex,
|
false => DocStyle::ModuleIndex,
|
||||||
};
|
};
|
||||||
Sidebar {
|
Sidebar {
|
||||||
|
@ -1006,8 +1014,8 @@ impl Renderable for ModuleIndex {
|
||||||
let doc_links = self.module_docs.clone().render()?;
|
let doc_links = self.module_docs.clone().render()?;
|
||||||
let sidebar = self.sidebar().render()?;
|
let sidebar = self.sidebar().render()?;
|
||||||
let title_prefix = match self.module_docs.style {
|
let title_prefix = match self.module_docs.style {
|
||||||
DocStyle::ProjectIndex => "Project ",
|
DocStyle::ProjectIndex(ref program_type) => format!("{program_type} "),
|
||||||
DocStyle::ModuleIndex => "Module ",
|
DocStyle::ModuleIndex => "Module ".to_string(),
|
||||||
_ => unreachable!("Module Index can only be either a project or module at this time."),
|
_ => unreachable!("Module Index can only be either a project or module at this time."),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1079,6 +1087,16 @@ impl Renderable for ModuleIndex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ if self.module_info.attributes.is_some() {
|
||||||
|
details(class="swaydoc-toggle top-doc", open) {
|
||||||
|
summary(class="hideme") {
|
||||||
|
span { : "Expand description" }
|
||||||
|
}
|
||||||
|
div(class="docblock") {
|
||||||
|
: Raw(self.module_info.attributes.unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
: doc_links;
|
: doc_links;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1094,8 +1112,8 @@ trait SidebarNav {
|
||||||
}
|
}
|
||||||
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
|
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
enum DocStyle {
|
enum DocStyle {
|
||||||
AllDoc,
|
AllDoc(String),
|
||||||
ProjectIndex,
|
ProjectIndex(String),
|
||||||
ModuleIndex,
|
ModuleIndex,
|
||||||
Item,
|
Item,
|
||||||
}
|
}
|
||||||
|
@ -1115,8 +1133,8 @@ impl Renderable for Sidebar {
|
||||||
.module_info
|
.module_info
|
||||||
.to_html_shorthand_path_string("assets/sway-logo.svg");
|
.to_html_shorthand_path_string("assets/sway-logo.svg");
|
||||||
let location_with_prefix = match &self.style {
|
let location_with_prefix = match &self.style {
|
||||||
DocStyle::AllDoc | DocStyle::ProjectIndex => {
|
DocStyle::AllDoc(project_kind) | DocStyle::ProjectIndex(project_kind) => {
|
||||||
format!("Project {}", self.module_info.location())
|
format!("{project_kind} {}", self.module_info.location())
|
||||||
}
|
}
|
||||||
DocStyle::ModuleIndex | DocStyle::Item => format!(
|
DocStyle::ModuleIndex | DocStyle::Item => format!(
|
||||||
"{} {}",
|
"{} {}",
|
||||||
|
@ -1125,15 +1143,17 @@ impl Renderable for Sidebar {
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
let (logo_path_to_parent, path_to_parent_or_self) = match &self.style {
|
let (logo_path_to_parent, path_to_parent_or_self) = match &self.style {
|
||||||
DocStyle::AllDoc | DocStyle::Item => (self.href_path.clone(), self.href_path.clone()),
|
DocStyle::AllDoc(_) | DocStyle::Item => {
|
||||||
DocStyle::ProjectIndex => (IDENTITY.to_owned(), IDENTITY.to_owned()),
|
(self.href_path.clone(), self.href_path.clone())
|
||||||
|
}
|
||||||
|
DocStyle::ProjectIndex(_) => (IDENTITY.to_owned(), IDENTITY.to_owned()),
|
||||||
DocStyle::ModuleIndex => (format!("../{INDEX_FILENAME}"), IDENTITY.to_owned()),
|
DocStyle::ModuleIndex => (format!("../{INDEX_FILENAME}"), IDENTITY.to_owned()),
|
||||||
};
|
};
|
||||||
// Unfortunately, match arms that return a closure, even if they are the same
|
// Unfortunately, match arms that return a closure, even if they are the same
|
||||||
// type, are incompatible. The work around is to return a String instead,
|
// type, are incompatible. The work around is to return a String instead,
|
||||||
// and render it from Raw in the final output.
|
// and render it from Raw in the final output.
|
||||||
let styled_content = match &self.style {
|
let styled_content = match &self.style {
|
||||||
DocStyle::ProjectIndex => {
|
DocStyle::ProjectIndex(_) => {
|
||||||
let nav_links = self.nav.links;
|
let nav_links = self.nav.links;
|
||||||
let version = match self.version_opt {
|
let version = match self.version_opt {
|
||||||
Some(ref v) => v.as_str(),
|
Some(ref v) => v.as_str(),
|
||||||
|
|
|
@ -465,6 +465,15 @@ impl TyProgramKind {
|
||||||
TyProgramKind::Script { .. } => parsed::TreeType::Script,
|
TyProgramKind::Script { .. } => parsed::TreeType::Script,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Used for project titles in `forc doc`.
|
||||||
|
pub fn as_title_str(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
TyProgramKind::Contract { .. } => "Contract",
|
||||||
|
TyProgramKind::Library { .. } => "Library",
|
||||||
|
TyProgramKind::Predicate { .. } => "Predicate",
|
||||||
|
TyProgramKind::Script { .. } => "Script",
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn disallow_impure_functions(
|
fn disallow_impure_functions(
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//! The official standard library for the Sway smart contract language.
|
||||||
library std;
|
library std;
|
||||||
|
|
||||||
dep error_signals;
|
dep error_signals;
|
||||||
|
|
|
@ -498,7 +498,7 @@ async fn go_to_definition_for_paths() {
|
||||||
req_uri: &uri,
|
req_uri: &uri,
|
||||||
req_line: 10,
|
req_line: 10,
|
||||||
req_char: 13,
|
req_char: 13,
|
||||||
def_line: 0,
|
def_line: 1,
|
||||||
def_start_char: 8,
|
def_start_char: 8,
|
||||||
def_end_char: 11,
|
def_end_char: 11,
|
||||||
def_path: "sway-lib-std/src/lib.sw",
|
def_path: "sway-lib-std/src/lib.sw",
|
||||||
|
|
|
@ -25,7 +25,11 @@ pub use crate::{
|
||||||
token::{lex, lex_commented},
|
token::{lex, lex_commented},
|
||||||
};
|
};
|
||||||
|
|
||||||
use sway_ast::{attribute::Annotated, Module, ModuleKind};
|
use sway_ast::{
|
||||||
|
attribute::Annotated,
|
||||||
|
token::{DocComment, DocStyle},
|
||||||
|
Module, ModuleKind,
|
||||||
|
};
|
||||||
use sway_error::handler::{ErrorEmitted, Handler};
|
use sway_error::handler::{ErrorEmitted, Handler};
|
||||||
|
|
||||||
use std::{path::PathBuf, sync::Arc};
|
use std::{path::PathBuf, sync::Arc};
|
||||||
|
@ -45,5 +49,13 @@ pub fn parse_module_kind(
|
||||||
path: Option<Arc<PathBuf>>,
|
path: Option<Arc<PathBuf>>,
|
||||||
) -> Result<ModuleKind, ErrorEmitted> {
|
) -> Result<ModuleKind, ErrorEmitted> {
|
||||||
let ts = lex(handler, &src, 0, src.len(), path)?;
|
let ts = lex(handler, &src, 0, src.len(), path)?;
|
||||||
Parser::new(handler, &ts).parse()
|
let mut parser = Parser::new(handler, &ts);
|
||||||
|
while let Some(DocComment {
|
||||||
|
doc_style: DocStyle::Inner,
|
||||||
|
..
|
||||||
|
}) = parser.peek()
|
||||||
|
{
|
||||||
|
parser.parse::<DocComment>()?;
|
||||||
|
}
|
||||||
|
parser.parse()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue