Wrap code snippets in Code struct

In commit 729414cbfe16324379ac0b886ba65349bbf44ff6, we introduced the
Text struct that wraps HTML and plain text to make it clear what data we
store in the fields of the Doc and Example structs.  This patch adds the
Code struct that stores a plain-text representation of code snippets to
make the structs even clearer and to make it easier to transition to
plain-text-only code, e. g. provided by the new JSON backend.
This commit is contained in:
Robin Krahl 2020-08-17 12:01:23 +02:00
parent fefac9f2fc
commit 861a2e8861
No known key found for this signature in database
GPG Key ID: 8E9B0870524F69D8
5 changed files with 58 additions and 50 deletions

View File

@ -26,6 +26,9 @@ pub struct Text {
pub html: String,
}
#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct Code(String);
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, serde_repr::Deserialize_repr)]
#[repr(u8)]
pub enum ItemType {
@ -62,7 +65,7 @@ pub struct Doc {
pub name: Fqn,
pub ty: ItemType,
pub description: Option<Text>,
pub definition: Option<Text>,
pub definition: Option<Code>,
pub groups: Vec<(ItemType, Vec<MemberGroup>)>,
}
@ -75,7 +78,7 @@ pub struct MemberGroup {
#[derive(Clone, Debug)]
pub struct Example {
pub description: Option<Text>,
pub code: Text,
pub code: Code,
}
impl Name {
@ -211,6 +214,26 @@ impl ops::Deref for Fqn {
}
}
impl Code {
pub fn new(s: String) -> Code {
Code(s)
}
}
impl fmt::Display for Code {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", &self.0)
}
}
impl ops::Deref for Code {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ItemType {
pub fn name(&self) -> &str {
match self {
@ -273,37 +296,6 @@ impl ItemType {
ItemType::TraitAlias => "Trait Aliases",
}
}
pub fn group_id(&self) -> &str {
match self {
ItemType::Module => "modules",
ItemType::ExternCrate => "extern-crates",
ItemType::Import => "imports",
ItemType::Struct => "structs",
ItemType::Enum => "enums",
ItemType::Function => "functions",
ItemType::Typedef => "types",
ItemType::Static => "statics",
ItemType::Trait => "traits",
ItemType::Impl => "impls",
ItemType::TyMethod => "required-methods",
ItemType::Method => "methods",
ItemType::StructField => "fields",
ItemType::Variant => "variants",
ItemType::Macro => "macros",
ItemType::Primitive => "primitives",
ItemType::AssocType => "associated-types",
ItemType::Constant => "constants",
ItemType::AssocConst => "associated-consts",
ItemType::Union => "unions",
ItemType::ForeignType => "foreign-types",
ItemType::Keyword => "keywords",
ItemType::OpaqueTy => "opaque-types",
ItemType::ProcAttribute => "proc-attributes",
ItemType::ProcDerive => "proc-derives",
ItemType::TraitAlias => "trait-aliases",
}
}
}
impl str::FromStr for ItemType {
@ -382,7 +374,7 @@ impl MemberGroup {
}
impl Example {
pub fn new(description: Option<Text>, code: Text) -> Self {
pub fn new(description: Option<Text>, code: Code) -> Self {
Example { description, code }
}
}

View File

@ -158,6 +158,24 @@ impl<T> From<kuchiki::NodeDataRef<T>> for doc::Text {
}
}
impl From<kuchiki::NodeRef> for doc::Code {
fn from(node: kuchiki::NodeRef) -> doc::Code {
doc::Code::from(&node)
}
}
impl From<&kuchiki::NodeRef> for doc::Code {
fn from(node: &kuchiki::NodeRef) -> doc::Code {
doc::Code::new(node_to_text(node))
}
}
impl<T> From<kuchiki::NodeDataRef<T>> for doc::Code {
fn from(node: kuchiki::NodeDataRef<T>) -> doc::Code {
node.as_node().into()
}
}
fn node_to_text(node: &kuchiki::NodeRef) -> String {
let mut s = String::new();
push_node_to_text(&mut s, node);
@ -272,7 +290,7 @@ fn get_fields(
let mut next = heading.as_ref().and_then(NodeRefExt::next_sibling_element);
let mut name: Option<String> = None;
let mut definition: Option<doc::Text> = None;
let mut definition: Option<doc::Code> = None;
while let Some(element) = &next {
if element.is_element(&local_name!("span")) && element.has_class("structfield") {
@ -449,7 +467,7 @@ fn get_method_group(
let mut methods = MemberDocs::new(parent, ty);
let mut name: Option<String> = None;
let mut definition: Option<doc::Text> = None;
let mut definition: Option<doc::Code> = None;
for element in impl_items.children() {
if element.is_element(heading_type) && element.has_class("method") {
methods.push(&mut name, &mut definition, None)?;
@ -473,7 +491,7 @@ fn get_variants(
let mut next = heading.as_ref().and_then(NodeRefExt::next_sibling_element);
let mut name: Option<String> = None;
let mut definition: Option<doc::Text> = None;
let mut definition: Option<doc::Code> = None;
while let Some(element) = &next {
if element.is_element(&local_name!("div")) {
if element.has_class("variant") {
@ -604,7 +622,7 @@ impl<'a> MemberDocs<'a> {
pub fn push(
&mut self,
name: &mut Option<String>,
definition: &mut Option<doc::Text>,
definition: &mut Option<doc::Code>,
description: Option<doc::Text>,
) -> anyhow::Result<()> {
let name = name.take();
@ -719,13 +737,9 @@ mod tests {
assert_eq!(name, doc.name);
assert_eq!(doc::ItemType::Method, doc.ty);
let definition = doc.definition.unwrap();
assert_eq!("pub fn as_node(&self) -> &NodeRef", &definition.plain);
assert_eq!(
"<code id=\"as_node.v\">\
pub fn <a class=\"fnname\" href=\"#method.as_node\">as_node</a>(&amp;self) \
-&gt; &amp;<a class=\"struct\" href=\"../kuchiki/struct.NodeRef.html\" \
title=\"struct kuchiki::NodeRef\">NodeRef</a></code>",
&definition.html
doc::Code::new("pub fn as_node(&self) -> &NodeRef".to_owned()),
definition
);
assert!(doc.description.is_some());
}

View File

@ -21,7 +21,7 @@ pub trait Printer: fmt::Debug + marker::Sized {
fn print_html(&self, indent: usize, s: &doc::Text, show_links: bool) -> io::Result<()>;
fn print_code(&self, indent: usize, code: &doc::Text) -> io::Result<()>;
fn print_code(&self, indent: usize, code: &doc::Code) -> io::Result<()>;
fn println(&self) -> io::Result<()>;
}

View File

@ -44,8 +44,8 @@ impl super::Printer for PlainTextRenderer {
Ok(())
}
fn print_code(&self, indent: usize, code: &doc::Text) -> io::Result<()> {
for line in code.plain.split('\n') {
fn print_code(&self, indent: usize, code: &doc::Code) -> io::Result<()> {
for line in code.split('\n') {
writeln!(io::stdout(), "{}{}", " ".repeat(indent), line)?;
}
Ok(())

View File

@ -97,19 +97,21 @@ impl super::Printer for RichTextRenderer {
Ok(())
}
fn print_code(&self, indent: usize, code: &doc::Text) -> io::Result<()> {
fn print_code(&self, indent: usize, code: &doc::Code) -> io::Result<()> {
if self.highlight {
let syntax = self.syntax_set.find_syntax_by_extension("rs").unwrap();
let mut h = syntect::easy::HighlightLines::new(syntax, &self.theme);
for line in syntect::util::LinesWithEndings::from(&code.plain) {
for line in syntect::util::LinesWithEndings::from(code.as_ref()) {
let ranges = h.highlight(line, &self.syntax_set);
write!(io::stdout(), "{}", " ".repeat(indent))?;
self.render_syntax(&ranges)?;
}
writeln!(io::stdout())?;
} else {
self.print_html(indent, code, false)?;
for line in code.split('\n') {
writeln!(io::stdout(), "{}{}", " ".repeat(indent), line)?;
}
}
Ok(())