2014-01-25 01:37:51 -06:00
|
|
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
2013-09-12 20:10:51 -05:00
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2014-05-29 21:03:06 -05:00
|
|
|
use std::collections::HashSet;
|
2014-04-02 18:54:22 -05:00
|
|
|
use rustc::util::nodemap::NodeSet;
|
|
|
|
use std::cmp;
|
2014-05-22 18:57:53 -05:00
|
|
|
use std::string::String;
|
2015-02-09 18:33:19 -06:00
|
|
|
use std::usize;
|
2013-09-24 15:53:09 -05:00
|
|
|
use syntax::ast;
|
2014-05-12 15:40:11 -05:00
|
|
|
use syntax::ast_util;
|
2013-09-24 15:53:09 -05:00
|
|
|
|
2013-08-15 15:28:54 -05:00
|
|
|
use clean;
|
|
|
|
use clean::Item;
|
|
|
|
use plugins;
|
|
|
|
use fold;
|
|
|
|
use fold::DocFolder;
|
|
|
|
|
|
|
|
/// Strip items marked `#[doc(hidden)]`
|
2014-02-05 15:15:24 -06:00
|
|
|
pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult {
|
2014-02-11 16:42:23 -06:00
|
|
|
let mut stripped = HashSet::new();
|
|
|
|
|
|
|
|
// strip all #[doc(hidden)] items
|
|
|
|
let krate = {
|
|
|
|
struct Stripper<'a> {
|
|
|
|
stripped: &'a mut HashSet<ast::NodeId>
|
|
|
|
};
|
|
|
|
impl<'a> fold::DocFolder for Stripper<'a> {
|
|
|
|
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
2014-04-28 13:59:48 -05:00
|
|
|
if i.is_hidden_from_doc() {
|
|
|
|
debug!("found one in strip_hidden; removing");
|
2014-05-03 04:08:58 -05:00
|
|
|
self.stripped.insert(i.def_id.node);
|
2014-04-28 13:59:48 -05:00
|
|
|
|
|
|
|
// use a dedicated hidden item for given item type if any
|
|
|
|
match i.inner {
|
|
|
|
clean::StructFieldItem(..) => {
|
|
|
|
return Some(clean::Item {
|
|
|
|
inner: clean::StructFieldItem(clean::HiddenStructField),
|
|
|
|
..i
|
|
|
|
});
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
return None;
|
|
|
|
}
|
2014-02-11 16:42:23 -06:00
|
|
|
}
|
|
|
|
}
|
2014-04-28 13:59:48 -05:00
|
|
|
|
2014-02-11 16:42:23 -06:00
|
|
|
self.fold_item_recur(i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let mut stripper = Stripper{ stripped: &mut stripped };
|
|
|
|
stripper.fold_crate(krate)
|
|
|
|
};
|
|
|
|
|
|
|
|
// strip any traits implemented on stripped items
|
|
|
|
let krate = {
|
|
|
|
struct ImplStripper<'a> {
|
|
|
|
stripped: &'a mut HashSet<ast::NodeId>
|
|
|
|
};
|
|
|
|
impl<'a> fold::DocFolder for ImplStripper<'a> {
|
|
|
|
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
2014-11-29 15:41:21 -06:00
|
|
|
if let clean::ImplItem(clean::Impl{
|
|
|
|
for_: clean::ResolvedPath{ did, .. },
|
|
|
|
ref trait_, ..
|
|
|
|
}) = i.inner {
|
|
|
|
// Impls for stripped types don't need to exist
|
|
|
|
if self.stripped.contains(&did.node) {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
// Impls of stripped traits also don't need to exist
|
|
|
|
if let Some(clean::ResolvedPath { did, .. }) = *trait_ {
|
2014-05-09 15:52:17 -05:00
|
|
|
if self.stripped.contains(&did.node) {
|
2014-02-11 16:42:23 -06:00
|
|
|
return None;
|
2013-08-15 15:28:54 -05:00
|
|
|
}
|
2014-02-11 16:42:23 -06:00
|
|
|
}
|
2013-08-15 15:28:54 -05:00
|
|
|
}
|
2014-02-11 16:42:23 -06:00
|
|
|
self.fold_item_recur(i)
|
2013-08-15 15:28:54 -05:00
|
|
|
}
|
|
|
|
}
|
2014-02-11 16:42:23 -06:00
|
|
|
let mut stripper = ImplStripper{ stripped: &mut stripped };
|
|
|
|
stripper.fold_crate(krate)
|
|
|
|
};
|
|
|
|
|
2014-02-05 15:15:24 -06:00
|
|
|
(krate, None)
|
2013-08-15 15:28:54 -05:00
|
|
|
}
|
|
|
|
|
2013-09-24 15:55:22 -05:00
|
|
|
/// Strip private items from the point of view of a crate or externally from a
|
|
|
|
/// crate, specified by the `xcrate` flag.
|
2014-04-28 22:36:08 -05:00
|
|
|
pub fn strip_private(mut krate: clean::Crate) -> plugins::PluginResult {
|
2013-09-26 14:27:38 -05:00
|
|
|
// This stripper collects all *retained* nodes.
|
2013-10-12 16:40:41 -05:00
|
|
|
let mut retained = HashSet::new();
|
2014-11-14 16:20:57 -06:00
|
|
|
let analysis = super::ANALYSISKEY.with(|a| a.clone());
|
|
|
|
let analysis = analysis.borrow();
|
|
|
|
let analysis = analysis.as_ref().unwrap();
|
2014-04-28 22:36:08 -05:00
|
|
|
let exported_items = analysis.exported_items.clone();
|
2013-10-12 16:40:41 -05:00
|
|
|
|
|
|
|
// strip all private items
|
|
|
|
{
|
|
|
|
let mut stripper = Stripper {
|
|
|
|
retained: &mut retained,
|
|
|
|
exported_items: &exported_items,
|
|
|
|
};
|
2014-02-05 15:15:24 -06:00
|
|
|
krate = stripper.fold_crate(krate);
|
2013-10-12 16:40:41 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// strip all private implementations of traits
|
|
|
|
{
|
|
|
|
let mut stripper = ImplStripper(&retained);
|
2014-02-05 15:15:24 -06:00
|
|
|
krate = stripper.fold_crate(krate);
|
2013-10-12 16:40:41 -05:00
|
|
|
}
|
2014-02-05 15:15:24 -06:00
|
|
|
(krate, None)
|
2013-10-12 16:40:41 -05:00
|
|
|
}
|
2013-09-24 15:55:22 -05:00
|
|
|
|
2013-12-10 01:16:18 -06:00
|
|
|
struct Stripper<'a> {
|
|
|
|
retained: &'a mut HashSet<ast::NodeId>,
|
2014-02-28 16:34:26 -06:00
|
|
|
exported_items: &'a NodeSet,
|
2013-10-12 16:40:41 -05:00
|
|
|
}
|
2013-09-24 15:55:22 -05:00
|
|
|
|
2013-12-10 01:16:18 -06:00
|
|
|
impl<'a> fold::DocFolder for Stripper<'a> {
|
2013-10-12 16:40:41 -05:00
|
|
|
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
|
|
|
match i.inner {
|
|
|
|
// These items can all get re-exported
|
2013-11-28 14:22:53 -06:00
|
|
|
clean::TypedefItem(..) | clean::StaticItem(..) |
|
|
|
|
clean::StructItem(..) | clean::EnumItem(..) |
|
|
|
|
clean::TraitItem(..) | clean::FunctionItem(..) |
|
|
|
|
clean::VariantItem(..) | clean::MethodItem(..) |
|
2014-10-07 10:36:55 -05:00
|
|
|
clean::ForeignFunctionItem(..) | clean::ForeignStaticItem(..) => {
|
|
|
|
if ast_util::is_local(i.def_id) {
|
|
|
|
if !self.exported_items.contains(&i.def_id.node) {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
// Traits are in exported_items even when they're totally private.
|
|
|
|
if i.is_trait() && i.visibility != Some(ast::Public) {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-06 19:41:15 -05:00
|
|
|
clean::ConstantItem(..) => {
|
2014-05-03 04:08:58 -05:00
|
|
|
if ast_util::is_local(i.def_id) &&
|
|
|
|
!self.exported_items.contains(&i.def_id.node) {
|
2013-10-12 16:40:41 -05:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
}
|
2013-09-24 15:55:22 -05:00
|
|
|
|
2014-12-26 02:55:16 -06:00
|
|
|
clean::ExternCrateItem(..) | clean::ImportItem(_) => {
|
2014-01-09 07:05:33 -06:00
|
|
|
if i.visibility != Some(ast::Public) {
|
2013-10-19 00:00:08 -05:00
|
|
|
return None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-20 00:24:52 -05:00
|
|
|
clean::StructFieldItem(..) => {
|
|
|
|
if i.visibility != Some(ast::Public) {
|
|
|
|
return Some(clean::Item {
|
|
|
|
inner: clean::StructFieldItem(clean::HiddenStructField),
|
|
|
|
..i
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-27 18:00:14 -05:00
|
|
|
// handled below
|
|
|
|
clean::ModuleItem(..) => {}
|
|
|
|
|
2014-02-11 16:29:23 -06:00
|
|
|
// trait impls for private items should be stripped
|
2014-05-09 15:52:17 -05:00
|
|
|
clean::ImplItem(clean::Impl{
|
|
|
|
for_: clean::ResolvedPath{ did, .. }, ..
|
|
|
|
}) => {
|
2014-05-23 02:42:33 -05:00
|
|
|
if ast_util::is_local(did) &&
|
|
|
|
!self.exported_items.contains(&did.node) {
|
2014-02-11 16:29:23 -06:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
}
|
2015-03-12 21:15:52 -05:00
|
|
|
clean::DefaultImplItem(..) | clean::ImplItem(..) => {}
|
2014-02-11 16:29:23 -06:00
|
|
|
|
2014-02-16 23:40:26 -06:00
|
|
|
// tymethods/macros have no control over privacy
|
|
|
|
clean::MacroItem(..) | clean::TyMethodItem(..) => {}
|
2014-05-28 21:53:37 -05:00
|
|
|
|
|
|
|
// Primitives are never stripped
|
|
|
|
clean::PrimitiveItem(..) => {}
|
2014-08-05 21:44:21 -05:00
|
|
|
|
2015-03-14 13:05:00 -05:00
|
|
|
// Associated consts and types are never stripped
|
|
|
|
clean::AssociatedConstItem(..) |
|
2014-08-05 21:44:21 -05:00
|
|
|
clean::AssociatedTypeItem(..) => {}
|
2013-10-12 16:40:41 -05:00
|
|
|
}
|
2013-09-24 15:55:22 -05:00
|
|
|
|
2013-10-12 16:40:41 -05:00
|
|
|
let fastreturn = match i.inner {
|
|
|
|
// nothing left to do for traits (don't want to filter their
|
|
|
|
// methods out, visibility controlled by the trait)
|
2013-11-28 14:22:53 -06:00
|
|
|
clean::TraitItem(..) => true,
|
2013-09-24 15:55:22 -05:00
|
|
|
|
2013-10-12 16:40:41 -05:00
|
|
|
// implementations of traits are always public.
|
|
|
|
clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
|
2013-09-24 15:55:22 -05:00
|
|
|
|
2014-11-26 17:57:22 -06:00
|
|
|
// Struct variant fields have inherited visibility
|
|
|
|
clean::VariantItem(clean::Variant {
|
|
|
|
kind: clean::StructVariant(..)
|
|
|
|
}) => true,
|
2013-10-12 16:40:41 -05:00
|
|
|
_ => false,
|
|
|
|
};
|
|
|
|
|
|
|
|
let i = if fastreturn {
|
2014-05-03 04:08:58 -05:00
|
|
|
self.retained.insert(i.def_id.node);
|
2013-10-12 16:40:41 -05:00
|
|
|
return Some(i);
|
|
|
|
} else {
|
|
|
|
self.fold_item_recur(i)
|
|
|
|
};
|
|
|
|
|
|
|
|
match i {
|
|
|
|
Some(i) => {
|
|
|
|
match i.inner {
|
|
|
|
// emptied modules/impls have no need to exist
|
2014-01-10 01:23:10 -06:00
|
|
|
clean::ModuleItem(ref m)
|
2015-03-24 18:53:34 -05:00
|
|
|
if m.items.is_empty() &&
|
2014-01-10 01:23:10 -06:00
|
|
|
i.doc_value().is_none() => None,
|
2015-03-24 18:53:34 -05:00
|
|
|
clean::ImplItem(ref i) if i.items.is_empty() => None,
|
2013-10-12 16:40:41 -05:00
|
|
|
_ => {
|
2014-05-03 04:08:58 -05:00
|
|
|
self.retained.insert(i.def_id.node);
|
2013-10-12 16:40:41 -05:00
|
|
|
Some(i)
|
2013-09-24 15:55:22 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-10-12 16:40:41 -05:00
|
|
|
None => None,
|
2013-09-24 15:55:22 -05:00
|
|
|
}
|
|
|
|
}
|
2013-10-12 16:40:41 -05:00
|
|
|
}
|
2013-09-26 14:27:38 -05:00
|
|
|
|
2013-10-12 16:40:41 -05:00
|
|
|
// This stripper discards all private impls of traits
|
2013-12-10 01:16:18 -06:00
|
|
|
struct ImplStripper<'a>(&'a HashSet<ast::NodeId>);
|
|
|
|
impl<'a> fold::DocFolder for ImplStripper<'a> {
|
2013-10-12 16:40:41 -05:00
|
|
|
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
2014-11-29 15:41:21 -06:00
|
|
|
if let clean::ImplItem(ref imp) = i.inner {
|
|
|
|
match imp.trait_ {
|
|
|
|
Some(clean::ResolvedPath{ did, .. }) => {
|
|
|
|
let ImplStripper(s) = *self;
|
|
|
|
if ast_util::is_local(did) && !s.contains(&did.node) {
|
|
|
|
return None;
|
2013-09-26 14:27:38 -05:00
|
|
|
}
|
|
|
|
}
|
2014-11-29 15:41:21 -06:00
|
|
|
Some(..) | None => {}
|
2013-09-26 14:27:38 -05:00
|
|
|
}
|
|
|
|
}
|
2013-10-12 16:40:41 -05:00
|
|
|
self.fold_item_recur(i)
|
2013-09-26 14:27:38 -05:00
|
|
|
}
|
2013-09-24 15:55:22 -05:00
|
|
|
}
|
|
|
|
|
2013-10-12 16:40:41 -05:00
|
|
|
|
2014-02-05 15:15:24 -06:00
|
|
|
pub fn unindent_comments(krate: clean::Crate) -> plugins::PluginResult {
|
2013-08-15 15:28:54 -05:00
|
|
|
struct CommentCleaner;
|
|
|
|
impl fold::DocFolder for CommentCleaner {
|
|
|
|
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
|
|
|
let mut i = i;
|
2014-03-05 17:28:08 -06:00
|
|
|
let mut avec: Vec<clean::Attribute> = Vec::new();
|
2015-01-31 11:20:46 -06:00
|
|
|
for attr in &i.attrs {
|
2013-08-15 15:28:54 -05:00
|
|
|
match attr {
|
2014-05-12 15:44:59 -05:00
|
|
|
&clean::NameValue(ref x, ref s)
|
2014-11-27 13:19:27 -06:00
|
|
|
if "doc" == *x => {
|
2014-05-25 05:17:19 -05:00
|
|
|
avec.push(clean::NameValue("doc".to_string(),
|
2015-02-01 20:53:25 -06:00
|
|
|
unindent(s)))
|
2014-05-12 15:44:59 -05:00
|
|
|
}
|
2013-08-15 15:28:54 -05:00
|
|
|
x => avec.push(x.clone())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i.attrs = avec;
|
|
|
|
self.fold_item_recur(i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let mut cleaner = CommentCleaner;
|
2014-02-05 15:15:24 -06:00
|
|
|
let krate = cleaner.fold_crate(krate);
|
|
|
|
(krate, None)
|
2013-08-15 15:28:54 -05:00
|
|
|
}
|
|
|
|
|
2014-02-05 15:15:24 -06:00
|
|
|
pub fn collapse_docs(krate: clean::Crate) -> plugins::PluginResult {
|
2013-08-15 15:28:54 -05:00
|
|
|
struct Collapser;
|
|
|
|
impl fold::DocFolder for Collapser {
|
|
|
|
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
2014-05-22 18:57:53 -05:00
|
|
|
let mut docstr = String::new();
|
2013-08-15 15:28:54 -05:00
|
|
|
let mut i = i;
|
2015-01-31 11:20:46 -06:00
|
|
|
for attr in &i.attrs {
|
2013-08-15 15:28:54 -05:00
|
|
|
match *attr {
|
2014-05-12 15:44:59 -05:00
|
|
|
clean::NameValue(ref x, ref s)
|
2014-11-27 13:19:27 -06:00
|
|
|
if "doc" == *x => {
|
2015-02-01 20:53:25 -06:00
|
|
|
docstr.push_str(s);
|
2014-09-22 10:28:35 -05:00
|
|
|
docstr.push('\n');
|
2013-08-15 15:28:54 -05:00
|
|
|
},
|
|
|
|
_ => ()
|
|
|
|
}
|
|
|
|
}
|
2014-03-05 17:28:08 -06:00
|
|
|
let mut a: Vec<clean::Attribute> = i.attrs.iter().filter(|&a| match a {
|
2014-11-27 13:19:27 -06:00
|
|
|
&clean::NameValue(ref x, _) if "doc" == *x => false,
|
2013-08-15 15:28:54 -05:00
|
|
|
_ => true
|
2015-02-13 01:33:44 -06:00
|
|
|
}).cloned().collect();
|
2015-03-24 18:54:09 -05:00
|
|
|
if !docstr.is_empty() {
|
2014-05-25 05:17:19 -05:00
|
|
|
a.push(clean::NameValue("doc".to_string(), docstr));
|
2013-08-15 15:28:54 -05:00
|
|
|
}
|
|
|
|
i.attrs = a;
|
|
|
|
self.fold_item_recur(i)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let mut collapser = Collapser;
|
2014-02-05 15:15:24 -06:00
|
|
|
let krate = collapser.fold_crate(krate);
|
|
|
|
(krate, None)
|
2013-08-15 15:28:54 -05:00
|
|
|
}
|
|
|
|
|
2014-05-22 18:57:53 -05:00
|
|
|
pub fn unindent(s: &str) -> String {
|
2014-03-05 17:28:08 -06:00
|
|
|
let lines = s.lines_any().collect::<Vec<&str> >();
|
2013-09-19 00:18:38 -05:00
|
|
|
let mut saw_first_line = false;
|
|
|
|
let mut saw_second_line = false;
|
2015-02-09 18:33:19 -06:00
|
|
|
let min_indent = lines.iter().fold(usize::MAX, |min_indent, line| {
|
2013-09-19 00:18:38 -05:00
|
|
|
|
|
|
|
// After we see the first non-whitespace line, look at
|
|
|
|
// the line we have. If it is not whitespace, and therefore
|
|
|
|
// part of the first paragraph, then ignore the indentation
|
|
|
|
// level of the first line
|
|
|
|
let ignore_previous_indents =
|
|
|
|
saw_first_line &&
|
|
|
|
!saw_second_line &&
|
2014-12-10 21:46:38 -06:00
|
|
|
!line.chars().all(|c| c.is_whitespace());
|
2013-09-19 00:18:38 -05:00
|
|
|
|
|
|
|
let min_indent = if ignore_previous_indents {
|
2015-02-09 18:33:19 -06:00
|
|
|
usize::MAX
|
2013-09-19 00:18:38 -05:00
|
|
|
} else {
|
|
|
|
min_indent
|
|
|
|
};
|
2013-08-15 15:28:54 -05:00
|
|
|
|
2013-09-19 00:18:38 -05:00
|
|
|
if saw_first_line {
|
|
|
|
saw_second_line = true;
|
|
|
|
}
|
2013-08-15 15:28:54 -05:00
|
|
|
|
2014-12-10 21:46:38 -06:00
|
|
|
if line.chars().all(|c| c.is_whitespace()) {
|
2013-09-19 00:18:38 -05:00
|
|
|
min_indent
|
|
|
|
} else {
|
|
|
|
saw_first_line = true;
|
|
|
|
let mut spaces = 0;
|
2013-11-21 17:42:55 -06:00
|
|
|
line.chars().all(|char| {
|
2013-09-19 00:18:38 -05:00
|
|
|
// Only comparing against space because I wouldn't
|
|
|
|
// know what to do with mixed whitespace chars
|
|
|
|
if char == ' ' {
|
|
|
|
spaces += 1;
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
2013-11-21 17:42:55 -06:00
|
|
|
});
|
2014-02-06 01:34:33 -06:00
|
|
|
cmp::min(min_indent, spaces)
|
2013-09-19 00:18:38 -05:00
|
|
|
}
|
2013-11-21 17:42:55 -06:00
|
|
|
});
|
2013-09-19 00:18:38 -05:00
|
|
|
|
2015-03-24 18:54:09 -05:00
|
|
|
if !lines.is_empty() {
|
2014-07-14 18:37:25 -05:00
|
|
|
let mut unindented = vec![ lines[0].trim().to_string() ];
|
2015-07-11 06:34:57 -05:00
|
|
|
unindented.push_all(&lines[1..].iter().map(|&line| {
|
2014-12-10 21:46:38 -06:00
|
|
|
if line.chars().all(|c| c.is_whitespace()) {
|
2014-05-25 05:17:19 -05:00
|
|
|
line.to_string()
|
2014-02-13 11:46:46 -06:00
|
|
|
} else {
|
|
|
|
assert!(line.len() >= min_indent);
|
2015-01-17 18:15:52 -06:00
|
|
|
line[min_indent..].to_string()
|
2014-02-13 11:46:46 -06:00
|
|
|
}
|
2015-02-01 20:53:25 -06:00
|
|
|
}).collect::<Vec<_>>());
|
2015-07-10 07:19:21 -05:00
|
|
|
unindented.join("\n")
|
2014-02-13 11:46:46 -06:00
|
|
|
} else {
|
2014-05-25 05:17:19 -05:00
|
|
|
s.to_string()
|
2013-08-15 15:28:54 -05:00
|
|
|
}
|
2013-09-19 00:18:38 -05:00
|
|
|
}
|
2013-08-15 15:28:54 -05:00
|
|
|
|
2013-09-19 00:18:38 -05:00
|
|
|
#[cfg(test)]
|
|
|
|
mod unindent_tests {
|
|
|
|
use super::unindent;
|
2013-08-15 15:28:54 -05:00
|
|
|
|
2013-09-19 00:18:38 -05:00
|
|
|
#[test]
|
|
|
|
fn should_unindent() {
|
2014-05-25 05:10:11 -05:00
|
|
|
let s = " line1\n line2".to_string();
|
2015-02-01 20:53:25 -06:00
|
|
|
let r = unindent(&s);
|
2014-11-27 13:19:27 -06:00
|
|
|
assert_eq!(r, "line1\nline2");
|
2013-08-15 15:28:54 -05:00
|
|
|
}
|
|
|
|
|
2013-09-19 00:18:38 -05:00
|
|
|
#[test]
|
|
|
|
fn should_unindent_multiple_paragraphs() {
|
2014-05-25 05:10:11 -05:00
|
|
|
let s = " line1\n\n line2".to_string();
|
2015-02-01 20:53:25 -06:00
|
|
|
let r = unindent(&s);
|
2014-11-27 13:19:27 -06:00
|
|
|
assert_eq!(r, "line1\n\nline2");
|
2013-09-19 00:18:38 -05:00
|
|
|
}
|
2013-08-15 15:28:54 -05:00
|
|
|
|
2013-09-19 00:18:38 -05:00
|
|
|
#[test]
|
|
|
|
fn should_leave_multiple_indent_levels() {
|
|
|
|
// Line 2 is indented another level beyond the
|
|
|
|
// base indentation and should be preserved
|
2014-05-25 05:10:11 -05:00
|
|
|
let s = " line1\n\n line2".to_string();
|
2015-02-01 20:53:25 -06:00
|
|
|
let r = unindent(&s);
|
2014-11-27 13:19:27 -06:00
|
|
|
assert_eq!(r, "line1\n\n line2");
|
2013-08-15 15:28:54 -05:00
|
|
|
}
|
2013-09-12 14:11:06 -05:00
|
|
|
|
2013-09-19 00:18:38 -05:00
|
|
|
#[test]
|
|
|
|
fn should_ignore_first_line_indent() {
|
2014-06-08 23:00:52 -05:00
|
|
|
// The first line of the first paragraph may not be indented as
|
2013-09-19 00:18:38 -05:00
|
|
|
// far due to the way the doc string was written:
|
|
|
|
//
|
|
|
|
// #[doc = "Start way over here
|
|
|
|
// and continue here"]
|
2014-05-25 05:10:11 -05:00
|
|
|
let s = "line1\n line2".to_string();
|
2015-02-01 20:53:25 -06:00
|
|
|
let r = unindent(&s);
|
2014-11-27 13:19:27 -06:00
|
|
|
assert_eq!(r, "line1\nline2");
|
2013-08-15 15:28:54 -05:00
|
|
|
}
|
2013-09-12 14:11:06 -05:00
|
|
|
|
2013-09-19 00:18:38 -05:00
|
|
|
#[test]
|
|
|
|
fn should_not_ignore_first_line_indent_in_a_single_line_para() {
|
2014-05-25 05:10:11 -05:00
|
|
|
let s = "line1\n\n line2".to_string();
|
2015-02-01 20:53:25 -06:00
|
|
|
let r = unindent(&s);
|
2014-11-27 13:19:27 -06:00
|
|
|
assert_eq!(r, "line1\n\n line2");
|
2013-09-19 00:18:38 -05:00
|
|
|
}
|
2013-08-15 15:28:54 -05:00
|
|
|
}
|