diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c1a91f26dbf80..d41fbb46ac8d5 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1534,6 +1534,7 @@ impl Clean for ast::VariantKind { #[deriving(Clone, Encodable, Decodable)] pub struct Span { pub filename: String, + pub tag_match: Option, pub loline: uint, pub locol: uint, pub hiline: uint, @@ -1544,6 +1545,7 @@ impl Span { fn empty() -> Span { Span { filename: "".to_string(), + tag_match: None, loline: 0, locol: 0, hiline: 0, hicol: 0, } @@ -1556,8 +1558,13 @@ impl Clean for syntax::codemap::Span { let filename = cm.span_to_filename(*self); let lo = cm.lookup_char_pos(self.lo); let hi = cm.lookup_char_pos(self.hi); + let tm = { + let line = lo.line - 1; + lo.file.get_line(line as int) + }; Span { filename: filename.to_string(), + tag_match: Some(tm), loline: lo.line, locol: lo.col.to_uint(), hiline: hi.line, diff --git a/src/librustdoc/ctags.rs b/src/librustdoc/ctags.rs new file mode 100644 index 0000000000000..88d8b80877c3f --- /dev/null +++ b/src/librustdoc/ctags.rs @@ -0,0 +1,283 @@ +use std::io; +use std::fmt; +use std::io::{File}; +use clean; + +enum TagKind { + Macro, + Enumerator, + Enumeration, + Member, + Function, + Module, + Structure, + Variable, + Typedef, + Trait, +} + +impl fmt::Show for TagKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let c = match *self { + Macro => 'd', + Enumerator => 'g', + Enumeration => 'e', + Member => 'm', + Function => 'f', + Module => 'n', + Structure => 's', + Variable => 'v', + Typedef => 't', + Trait => 'i' + }; + + write!( f, "{}", c ) + } +} + +#[deriving(Clone)] +enum ExtraField { + StructName(String), + TraitName(String), +} + +impl fmt::Show for ExtraField { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + StructName(ref n) => write!( f, "struct:{}", n ), + TraitName(ref n) => write!( f, "trait:{}", n ) + } + } +} + +struct ExtraFields { + fields: Vec +} + +impl ExtraFields { + fn new( s: &[ExtraField] ) -> ExtraFields { + ExtraFields { fields: Vec::from_slice( s ) } + } +} + +impl fmt::Show for ExtraFields { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for (i, s) in self.fields.iter().enumerate() { + if i != 0 { + try!( write!( f, "\t" ) ); + } + try!( s.fmt( f ) ); + } + Ok(()) + } +} + + +struct Tag { + symbol: String, + file: String, + location: String, + kind: TagKind, + extra: ExtraFields +} + +impl Tag { + fn from_item( item: &clean::Item, kind: TagKind, extra: &[ExtraField] ) -> Tag { + let location = match item.source.tag_match { + Some(ref x) => x.clone(), + None => item.source.loline.to_string() + }; + Tag { + symbol: item.name.clone().expect( "unnamed item" ), + file: item.source.filename.clone(), + location: location, + kind: kind, + extra: ExtraFields::new(extra) + } + } +} + +impl PartialOrd for Tag { + fn partial_cmp( &self, other: &Tag ) -> Option { + match self.symbol.cmp( &other.symbol ) { + Equal => {}, + x => return Some(x) + } + + match self.file.cmp( &other.file ) { + Equal => {}, + x => return Some(x) + } + + + Some(self.location.cmp( &other.location )) + } +} + +impl Ord for Tag { + fn cmp( &self, other: &Tag ) -> Ordering { + self.partial_cmp( other ).unwrap() + } +} + + +impl PartialEq for Tag { + fn eq( &self, other: &Tag ) -> bool { + self.symbol.eq( &other.symbol ) && + self.file.eq( &other.file ) && + self.location.eq( &other.location ) + } +} + +impl Eq for Tag {} + +macro_rules! inner( + ($e:expr, $ty:ident) => ( + match $e.inner { + clean::$ty(ref x) => x, + _ => fail!( "expected `{}`", stringify!( $ty ) ) + } + ) +) + +fn handle_struct_item( item: &clean::Item, tags: &mut Vec ) { + let _struct = inner!( item, StructItem ); + let struct_name = item.name.clone().expect("unnamed struct"); + + tags.push( Tag::from_item( item, Structure, [] ) ); + + for item in _struct.fields.iter() { + if item.name.is_some() { + tags.push( Tag::from_item( + item, + Member, + &[ StructName(struct_name.clone()) ] + ) + ); + } + } +} + +fn handle_enum_item( item: &clean::Item, tags: &mut Vec ) { + let _enum = inner!( item, EnumItem ); + + tags.push( Tag::from_item( item, Enumeration, [] ) ); + + for item in _enum.variants.iter() { + if item.name.is_some() { + tags.push( Tag::from_item( + item, + Enumerator, + [] + ) + ); + } + } +} + +fn handle_trait_item( item: &clean::Item, tags: &mut Vec ) { + let _trait = inner!( item, TraitItem ); + let trait_name = item.name.clone().expect("unnamed trait"); + + tags.push( Tag::from_item( item, Trait, [] ) ); + + for ti in _trait.items.iter() { + let item = ti.item(); + tags.push( Tag::from_item( + item, + Member, + &[ TraitName(trait_name.clone()) ] + ) + ); + } +} + +fn handle_impl_item( item: &clean::Item, tags: &mut Vec ) { + let _impl = inner!( item, ImplItem ); + + for item in _impl.items.iter() { + tags.push( Tag::from_item( + item, + Function, + [] + ) + ); + } +} + + + +fn handle_module_item( item: &clean::Item, tags: &mut Vec ) { + let module = inner!( item, ModuleItem ); + + for item in module.items.iter() { + match item.inner { + clean::StructItem(..) => handle_struct_item( item, tags ), + clean::EnumItem(..) => handle_enum_item( item, tags ), + clean::ModuleItem(..) => handle_module_item( item, tags ), + clean::FunctionItem(..) => { + tags.push( Tag::from_item( item, Function, [] ) ); + }, + clean::TypedefItem(..) => { + tags.push( Tag::from_item( item, Typedef, [] ) ); + } + clean::StaticItem(..) => { + tags.push( Tag::from_item( item, Variable, [] ) ); + }, + clean::TraitItem(..) => handle_trait_item( item, tags ), + clean::ImplItem(..) => handle_impl_item( item, tags ), + clean::ViewItemItem(..) => {}, /* TODO - include crate/use aliases? */ + clean::TyMethodItem(..) => fail!( "ty method at module level" ), + clean::MethodItem(..) => fail!( "method at module level" ), + clean::StructFieldItem(..) => fail!( "struct field at module level" ), + clean::VariantItem(..) => fail!( "variant at modile level" ), + clean::ForeignFunctionItem(..) => { + tags.push( Tag::from_item( item, Function, [] ) ); + }, + clean::ForeignStaticItem(..) => { + tags.push( Tag::from_item( item, Variable, [] ) ); + }, + clean::MacroItem(..) => { + tags.push( Tag::from_item( item, Macro, [] ) ); + }, + clean::PrimitiveItem(..) => fail!( "primitive" ), + } + } +} + + + + +pub fn output(krate: clean::Crate, dst: Path) -> io::IoResult<()> { + let mut file = try!(File::create(&dst)); + + let mut tags = Vec::new(); + + let module_item = match krate.module { + Some(m) => m, + _ => return Ok(()) + }; + + handle_module_item( &module_item, &mut tags ); + + tags.sort(); + try!(writeln!(file, "!_TAG_FILE_FORMAT\t2")); + try!(writeln!(file, "!_TAG_FILE_SORTED\t1")); + try!(writeln!(file, "!_TAG_PROGRAM_NAME\trustdoc")); + try!(writeln!(file, "!_TAG_PROGRAM_URL\thttp://www.rust-lang.org/")); + try!(writeln!(file, "!_TAG_PROGRAM_VERSION\t{}", env!("CFG_VERSION"))); + + for tag in tags.iter() { + try!( + writeln!( file, "{}\t{}\t/^{}$/;\" {}\t{}", + tag.symbol, + tag.file, + tag.location, + tag.kind, + tag.extra + ) + ); + } + + Ok(()) +} diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 758af6758c2c4..49c45ab930430 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -39,6 +39,7 @@ pub use clean::SCHEMA_VERSION; pub mod clean; pub mod core; +pub mod ctags; pub mod doctree; #[macro_escape] pub mod externalfiles; @@ -139,7 +140,7 @@ pub fn opts() -> Vec { optopt("r", "input-format", "the input type of the specified file", "[rust|json]"), optopt("w", "output-format", "the output type to write", - "[html|json]"), + "[html|json|ctags]"), optopt("o", "output", "where to place the output", "PATH"), optopt("", "crate-name", "specify the name of this crate", "NAME"), optmulti("L", "library-path", "directory to add to crate search path", @@ -291,6 +292,12 @@ pub fn main_args(args: &[String]) -> int { Err(e) => fail!("failed to write json: {}", e), } } + Some("ctags") => { + match ctags::output(krate, output.unwrap_or(Path::new("TAGS"))) { + Ok(()) => {} + Err(e) => fail!("failed to write ctags: {}", e), + } + } Some(s) => { println!("unknown output format: {}", s); return 1;