Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prettydocs #2762

Merged
merged 3 commits into from
Jul 2, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/compiletest/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ fn load_errors(testfile: str) -> [expected_error]/~ {
}

fn parse_expected(line_num: uint, line: str) -> [expected_error]/~ unsafe {
let error_tag = "//!";
let error_tag = "//~";
let mut idx;
alt str::find_str(line, error_tag) {
option::none { ret []/~; }
option::some(nn) { idx = (nn as uint) + str::len(error_tag); }
}

// "//!^^^ kind msg" denotes a message expected
// "//~^^^ kind msg" denotes a message expected
// three lines above current line:
let mut adjust_line = 0u;
let len = str::len(line);
Expand Down
82 changes: 82 additions & 0 deletions src/etc/sugarise-doc-comments.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/python

#
# this script attempts to turn doc comment attributes (#[doc = "..."])
# into sugared-doc-comments (/** ... */ and /// ...)
#
# it sugarises all .rs/.rc files underneath the working directory
#

import sys, os, fnmatch, re


DOC_PATTERN = '^(?P<indent>[\\t ]*)#\\[(\\s*)doc(\\s*)=' + \
'(\\s*)"(?P<text>(\\"|[^"])*?)"(\\s*)\\]' + \
'(?P<semi>;)?'

ESCAPES = [("\\'", "'"),
('\\"', '"'),
("\\n", "\n"),
("\\r", "\r"),
("\\t", "\t")]


def unescape(s):
for (find, repl) in ESCAPES:
s = s.replace(find, repl)
return s


def block_trim(s):
lns = s.splitlines()

# remove leading/trailing whitespace-lines
while lns and not lns[0].strip():
lns = lns[1:]
while lns and not lns[-1].strip():
lns = lns[:-1]

# remove leading horizontal whitespace
n = sys.maxint
for ln in lns:
if ln.strip():
n = min(n, len(re.search('^\s*', ln).group()))
if n != sys.maxint:
lns = [ln[n:] for ln in lns]

# strip trailing whitespace
lns = [ln.rstrip() for ln in lns]

return lns


def replace_doc(m):
indent = m.group('indent')
text = block_trim(unescape(m.group('text')))

if len(text) > 1:
inner = '!' if m.group('semi') else '*'
starify = lambda s: indent + ' *' + (' ' + s if s else '')
text = '\n'.join(map(starify, text))
repl = indent + '/*' + inner + '\n' + text + '\n' + indent + ' */'
else:
inner = '!' if m.group('semi') else '/'
repl = indent + '//' + inner + ' ' + text[0]

return repl


def sugarise_file(path):
s = open(path).read()

r = re.compile(DOC_PATTERN, re.MULTILINE | re.DOTALL)
ns = re.sub(r, replace_doc, s)

if s != ns:
open(path, 'w').write(ns)


for (dirpath, dirnames, filenames) in os.walk('.'):
for name in fnmatch.filter(filenames, '*.r[sc]'):
sugarise_file(os.path.join(dirpath, name))

54 changes: 48 additions & 6 deletions src/libcore/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ export
all, any,
all_between, any_between,
map,
each,
each_char,
each, eachi,
each_char, each_chari,
bytes_iter,
chars_iter,
split_char_iter,
Expand All @@ -73,7 +73,7 @@ export
find_char, find_char_from, find_char_between,
rfind_char, rfind_char_from, rfind_char_between,
find_str, find_str_from, find_str_between,
contains,
contains, contains_char,
starts_with,
ends_with,

Expand Down Expand Up @@ -672,22 +672,35 @@ pure fn bytes_iter(ss: str/&, it: fn(u8)) {
#[doc = "Iterate over the bytes in a string"]
#[inline(always)]
pure fn each(s: str/&, it: fn(u8) -> bool) {
eachi(s, {|_i, b| it(b)})
}

#[doc = "Iterate over the bytes in a string, with indices"]
#[inline(always)]
pure fn eachi(s: str/&, it: fn(uint, u8) -> bool) {
let mut i = 0u, l = len(s);
while (i < l) {
if !it(s[i]) { break; }
if !it(i, s[i]) { break; }
i += 1u;
}
}

#[doc = "Iterates over the chars in a string"]
#[inline(always)]
pure fn each_char(s: str/&, it: fn(char) -> bool) {
let mut pos = 0u;
each_chari(s, {|_i, c| it(c)})
}

#[doc = "Iterates over the chars in a string, with indices"]
#[inline(always)]
pure fn each_chari(s: str/&, it: fn(uint, char) -> bool) {
let mut pos = 0u, ch_pos = 0u;
let len = len(s);
while pos < len {
let {ch, next} = char_range_at(s, pos);
pos = next;
if !it(ch) { break; }
if !it(ch_pos, ch) { break; }
ch_pos += 1u;
}
}

Expand Down Expand Up @@ -1146,6 +1159,18 @@ pure fn contains(haystack: str/&a, needle: str/&b) -> bool {
option::is_some(find_str(haystack, needle))
}

#[doc = "
Returns true if a string contains a char.

# Arguments

* haystack - The string to look in
* needle - The char to look for
"]
pure fn contains_char(haystack: str/&, needle: char) -> bool {
option::is_some(find_char(haystack, needle))
}

#[doc = "
Returns true if one string starts with another

Expand Down Expand Up @@ -1879,12 +1904,21 @@ impl extensions/& for str/& {
#[doc = "Returns true if one string contains another"]
#[inline]
fn contains(needle: str/&a) -> bool { contains(self, needle) }
#[doc = "Returns true if a string contains a char"]
#[inline]
fn contains_char(needle: char) -> bool { contains_char(self, needle) }
#[doc = "Iterate over the bytes in a string"]
#[inline]
fn each(it: fn(u8) -> bool) { each(self, it) }
#[doc = "Iterate over the bytes in a string, with indices"]
#[inline]
fn eachi(it: fn(uint, u8) -> bool) { eachi(self, it) }
#[doc = "Iterate over the chars in a string"]
#[inline]
fn each_char(it: fn(char) -> bool) { each_char(self, it) }
#[doc = "Iterate over the chars in a string, with indices"]
#[inline]
fn each_chari(it: fn(uint, char) -> bool) { each_chari(self, it) }
#[doc = "Returns true if one string ends with another"]
#[inline]
fn ends_with(needle: str/&) -> bool { ends_with(self, needle) }
Expand Down Expand Up @@ -2644,6 +2678,14 @@ mod tests {
assert !contains(data, "ไท华");
}

#[test]
fn test_contains_char() {
assert contains_char("abc", 'b');
assert contains_char("a", 'a');
assert !contains_char("abc", 'd');
assert !contains_char("", 'a');
}

#[test]
fn test_chars_iter() {
let mut i = 0;
Expand Down
3 changes: 2 additions & 1 deletion src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -652,8 +652,9 @@ type attribute = spanned<attribute_>;
#[auto_serialize]
enum attr_style { attr_outer, attr_inner, }

// doc-comments are promoted to attributes that have is_sugared_doc = true
#[auto_serialize]
type attribute_ = {style: attr_style, value: meta_item};
type attribute_ = {style: attr_style, value: meta_item, is_sugared_doc: bool};

/*
iface_refs appear in both impls and in classes that implement ifaces.
Expand Down
27 changes: 25 additions & 2 deletions src/libsyntax/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@ import std::map;
import std::map::hashmap;
import either::either;
import diagnostic::span_handler;
import ast_util::dummy_spanned;
import ast_util::{spanned, dummy_spanned};
import parse::comments::{doc_comment_style, strip_doc_comment_decoration};

// Constructors
export mk_name_value_item_str;
export mk_name_value_item;
export mk_list_item;
export mk_word_item;
export mk_attr;
export mk_sugared_doc_attr;

// Conversion
export attr_meta;
export attr_metas;
export desugar_doc_attr;

// Accessors
export get_attr_name;
Expand Down Expand Up @@ -66,9 +69,19 @@ fn mk_word_item(+name: ast::ident) -> @ast::meta_item {
}

fn mk_attr(item: @ast::meta_item) -> ast::attribute {
ret dummy_spanned({style: ast::attr_inner, value: *item});
ret dummy_spanned({style: ast::attr_inner, value: *item,
is_sugared_doc: false});
}

fn mk_sugared_doc_attr(text: str, lo: uint, hi: uint) -> ast::attribute {
let lit = spanned(lo, hi, ast::lit_str(@text));
let attr = {
style: doc_comment_style(text),
value: spanned(lo, hi, ast::meta_name_value(@"doc", lit)),
is_sugared_doc: true
};
ret spanned(lo, hi, attr);
}

/* Conversion */

Expand All @@ -81,6 +94,16 @@ fn attr_metas(attrs: [ast::attribute]/~) -> [@ast::meta_item]/~ {
ret mitems;
}

fn desugar_doc_attr(attr: ast::attribute) -> ast::attribute {
if attr.node.is_sugared_doc {
let comment = get_meta_item_value_str(@attr.node.value).get();
let meta = mk_name_value_item_str(@"doc",
strip_doc_comment_decoration(*comment));
ret mk_attr(meta);
} else {
attr
}
}

/* Accessors */

Expand Down
3 changes: 2 additions & 1 deletion src/libsyntax/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ fn fold_meta_item_(&&mi: @meta_item, fld: ast_fold) -> @meta_item {
fn fold_attribute_(at: attribute, fld: ast_fold) ->
attribute {
ret {node: {style: at.node.style,
value: *fold_meta_item_(@at.node.value, fld)},
value: *fold_meta_item_(@at.node.value, fld),
is_sugared_doc: at.node.is_sugared_doc },
span: fld.new_span(at.span)};
}
//used in noop_fold_foreign_item and noop_fold_fn_decl
Expand Down
Loading