diff --git a/src/Cargo.lock b/src/Cargo.lock index d153945dc091f..2a12828f433fd 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -349,6 +349,7 @@ dependencies = [ "rustc_const_eval 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", + "rustc_highlight 0.0.0", "rustc_incremental 0.0.0", "rustc_lint 0.0.0", "rustc_llvm 0.0.0", @@ -376,6 +377,15 @@ dependencies = [ "syntax_pos 0.0.0", ] +[[package]] +name = "rustc_highlight" +version = "0.0.0" +dependencies = [ + "log 0.0.0", + "syntax 0.0.0", + "syntax_pos 0.0.0", +] + [[package]] name = "rustc_i128" version = "0.0.0" @@ -572,6 +582,7 @@ dependencies = [ "rustc_data_structures 0.0.0", "rustc_driver 0.0.0", "rustc_errors 0.0.0", + "rustc_highlight 0.0.0", "rustc_lint 0.0.0", "rustc_metadata 0.0.0", "rustc_resolve 0.0.0", diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index 99d3e155e8936..93cdeda5b089f 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -32,6 +32,7 @@ rustc_resolve = { path = "../librustc_resolve" } rustc_save_analysis = { path = "../librustc_save_analysis" } rustc_trans = { path = "../librustc_trans" } rustc_typeck = { path = "../librustc_typeck" } +rustc_highlight = { path = "../librustc_highlight" } serialize = { path = "../libserialize" } syntax = { path = "../libsyntax" } syntax_ext = { path = "../libsyntax_ext" } diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 48eb6f68564f0..f059c5931b5d2 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -30,6 +30,7 @@ #![feature(rustc_private)] #![feature(set_stdio)] #![feature(staged_api)] +#![feature(rustc_highlight)] extern crate arena; extern crate flate; @@ -53,6 +54,7 @@ extern crate rustc_resolve; extern crate rustc_save_analysis; extern crate rustc_trans; extern crate rustc_typeck; +extern crate rustc_highlight; extern crate serialize; extern crate rustc_llvm as llvm; #[macro_use] @@ -78,6 +80,7 @@ use rustc::lint::Lint; use rustc::lint; use rustc_metadata::locator; use rustc_metadata::cstore::CStore; +use rustc_highlight::highlight::render_to_stdout_with_highlighting; use rustc::util::common::time; use serialize::json::ToJson; @@ -349,6 +352,43 @@ pub trait CompilerCalls<'a> { #[derive(Copy, Clone)] pub struct RustcDefaultCalls; +fn print_msg(text: &str) { + let mut code = String::new(); + let mut in_code = false; + let mut code_is_rust = false; + for (i, line) in text.split('\n').enumerate() { + // Slice off the leading newline and print. + if i == 0 && line.len() == 0 { + continue; + } + if line.starts_with("```") { + if in_code { + if code_is_rust { + println!("rust"); + render_to_stdout_with_highlighting(code); + } else { + render_to_stdout_with_highlighting(code); + } + code = String::new(); + code_is_rust = false; + } else { + if line.starts_with("```rust") { + code_is_rust = true; + } + } + in_code = !in_code; + println!("```"); + } else { + if in_code { + code.push_str(&line); + code.push_str("\n"); + } else { + println!("{}", line); + } + } + } +} + fn handle_explain(code: &str, descriptions: &errors::registry::Registry, output: ErrorOutputType) { @@ -359,14 +399,7 @@ fn handle_explain(code: &str, }; match descriptions.find_description(&normalised) { Some(ref description) => { - // Slice off the leading newline and print. - print!("{}", &(&description[1..]).split("\n").map(|x| { - format!("{}\n", if x.starts_with("```") { - "```" - } else { - x - }) - }).collect::()); + print_msg(description); } None => { early_error(output, &format!("no extended information for {}", code)); diff --git a/src/librustc_highlight/Cargo.toml b/src/librustc_highlight/Cargo.toml new file mode 100644 index 0000000000000..c205ed5ab396a --- /dev/null +++ b/src/librustc_highlight/Cargo.toml @@ -0,0 +1,14 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_highlight" +version = "0.0.0" + +[lib] +name = "rustc_highlight" +path = "lib.rs" +crate-type = ["dylib"] + +[dependencies] +syntax = { path = "../libsyntax" } +syntax_pos = { path = "../libsyntax_pos" } +log = { path = "../liblog" } diff --git a/src/librustdoc/html/escape.rs b/src/librustc_highlight/escape.rs similarity index 100% rename from src/librustdoc/html/escape.rs rename to src/librustc_highlight/escape.rs diff --git a/src/librustdoc/html/highlight.rs b/src/librustc_highlight/highlight.rs similarity index 73% rename from src/librustdoc/html/highlight.rs rename to src/librustc_highlight/highlight.rs index 0629e93e7ef5d..f547a07243cce 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustc_highlight/highlight.rs @@ -1,4 +1,4 @@ -// Copyright 2014-2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2014-2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -20,7 +20,7 @@ //! other then HTML), then you should implement the `Writer` trait and use a //! `Classifier`. -use html::escape::Escape; +use escape::Escape; use std::fmt::Display; use std::io; @@ -31,42 +31,8 @@ use syntax::parse::lexer::{self, TokenAndSpan}; use syntax::parse::token; use syntax::parse; use syntax_pos::Span; - -/// Highlights `src`, returning the HTML output. -pub fn render_with_highlighting(src: &str, class: Option<&str>, id: Option<&str>, - extension: Option<&str>) -> String { - debug!("highlighting: ================\n{}\n==============", src); - let sess = parse::ParseSess::new(); - let fm = sess.codemap().new_filemap("".to_string(), None, src.to_string()); - - let mut out = Vec::new(); - write_header(class, id, &mut out).unwrap(); - - let mut classifier = Classifier::new(lexer::StringReader::new(&sess, fm), sess.codemap()); - if let Err(_) = classifier.write_source(&mut out) { - return format!("
{}
", src); - } - - if let Some(extension) = extension { - write!(out, "{}", extension).unwrap(); - } - write_footer(&mut out).unwrap(); - String::from_utf8_lossy(&out[..]).into_owned() -} - -/// Highlights `src`, returning the HTML output. Returns only the inner html to -/// be inserted into an element. C.f., `render_with_highlighting` which includes -/// an enclosing `
` block.
-pub fn render_inner_with_highlighting(src: &str) -> io::Result {
-    let sess = parse::ParseSess::new();
-    let fm = sess.codemap().new_filemap("".to_string(), None, src.to_string());
-
-    let mut out = Vec::new();
-    let mut classifier = Classifier::new(lexer::StringReader::new(&sess, fm), sess.codemap());
-    classifier.write_source(&mut out)?;
-
-    Ok(String::from_utf8_lossy(&out).into_owned())
-}
+use term::{color, Terminal, stdout, Attr};
+use term::Attr::{ForegroundColor, Bold, Dim, Underline};
 
 /// Processes a program (nested in the internal `lexer`), classifying strings of
 /// text by highlighting category (`Class`). Calls out to a `Writer` to write
@@ -131,30 +97,11 @@ pub trait Writer {
     /// ```
     /// The latter can be thought of as a shorthand for the former, which is
     /// more flexible.
-    fn string(&mut self, T, Class, Option<&TokenAndSpan>) -> io::Result<()>;
-}
-
-// Implement `Writer` for anthing that can be written to, this just implements
-// the default rustdoc behaviour.
-impl Writer for U {
     fn string(&mut self,
                           text: T,
                           klass: Class,
                           _tas: Option<&TokenAndSpan>)
-                          -> io::Result<()> {
-        match klass {
-            Class::None => write!(self, "{}", text),
-            klass => write!(self, "{}", klass.rustdoc_class(), text),
-        }
-    }
-
-    fn enter_span(&mut self, klass: Class) -> io::Result<()> {
-        write!(self, "", klass.rustdoc_class())
-    }
-
-    fn exit_span(&mut self) -> io::Result<()> {
-        write!(self, "")
-    }
+                          -> io::Result<()>;
 }
 
 impl<'a> Classifier<'a> {
@@ -175,7 +122,7 @@ impl<'a> Classifier<'a> {
     /// is used. All source code emission is done as slices from the source map,
     /// not from the tokens themselves, in order to stay true to the original
     /// source.
-    pub fn write_source(&mut self,
+    pub fn write_source(&mut self,
                                    out: &mut W)
                                    -> io::Result<()> {
         loop {
@@ -202,13 +149,13 @@ impl<'a> Classifier<'a> {
     }
 
     // Handles an individual token from the lexer.
-    fn write_token(&mut self,
+    fn write_token(&mut self,
                               out: &mut W,
                               tas: TokenAndSpan)
                               -> io::Result<()> {
         let klass = match tas.tok {
             token::Shebang(s) => {
-                out.string(Escape(&s.as_str()), Class::None, Some(&tas))?;
+                out.string(&s.as_str(), Class::None, Some(&tas))?;
                 return Ok(());
             },
 
@@ -320,7 +267,7 @@ impl<'a> Classifier<'a> {
 
         // Anything that didn't return above is the simple case where we the
         // class just spans a single token, so we can use the `string` method.
-        out.string(Escape(&self.snip(tas.sp)), klass, Some(&tas))
+        out.string(&self.snip(tas.sp), klass, Some(&tas))
     }
 
     // Helper function to get a snippet from the codemap.
@@ -353,19 +300,141 @@ impl Class {
             Class::QuestionMark => "question-mark"
         }
     }
+
+    pub fn term_style(self) -> Vec {
+        match self {
+            Class::None => vec![],
+            Class::Comment => vec![ForegroundColor(color::GREEN)],
+            Class::DocComment => vec![ForegroundColor(color::MAGENTA)],
+            Class::Attribute => vec![Bold],
+            Class::KeyWord => vec![Dim, ForegroundColor(color::YELLOW)],
+            Class::RefKeyWord => vec![Dim, ForegroundColor(color::MAGENTA)],
+            Class::Self_ => vec![ForegroundColor(color::CYAN)],
+            Class::Op => vec![ForegroundColor(color::YELLOW)],
+            Class::Macro => vec![ForegroundColor(color::MAGENTA)],
+            Class::MacroNonTerminal => vec![ForegroundColor(color::MAGENTA), Bold],
+            Class::String => vec![Underline(true)],
+            Class::Number => vec![ForegroundColor(color::CYAN)],
+            Class::Bool => vec![ForegroundColor(color::CYAN)],
+            Class::Ident => vec![],
+            Class::Lifetime => vec![Dim, ForegroundColor(color::MAGENTA)],
+            Class::PreludeTy => vec![ForegroundColor(color::GREEN)],
+            Class::PreludeVal => vec![ForegroundColor(color::CYAN)],
+            Class::QuestionMark => vec![Bold, ForegroundColor(color::GREEN)],
+        }
+
+    }
+}
+
+// You can implement `Writer` for anthing that can be written to, this just implements
+// the default rustdoc behaviour for HTML output.
+impl Writer for Vec {
+    fn string(&mut self,
+                          text: T,
+                          klass: Class,
+                          _tas: Option<&TokenAndSpan>)
+                          -> io::Result<()> {
+        match klass {
+            Class::None => write!(self, "{}", text),
+            klass => write!(self,
+                            "{}",
+                            klass.rustdoc_class(),
+                            Escape(&format!("{}", text))),
+        }
+    }
+
+    fn enter_span(&mut self, klass: Class) -> io::Result<()> {
+        write!(self, "", klass.rustdoc_class())
+    }
+
+    fn exit_span(&mut self) -> io::Result<()> {
+        write!(self, "")
+    }
+}
+
+// This implements behaviour for terminal output.
+impl Writer for Box + Send> {
+    fn string(&mut self,
+                          text: T,
+                          klass: Class,
+                          _tas: Option<&TokenAndSpan>)
+                          -> io::Result<()> {
+        for attr in klass.term_style() {
+            self.attr(attr)?;
+        }
+        write!(self, "{}", text)?;
+        let _ = self.reset();
+        Ok(())
+    }
+
+    fn enter_span(&mut self, klass: Class) -> io::Result<()> {
+        for attr in klass.term_style() {
+            self.attr(attr)?;
+        }
+        Ok(())
+    }
+
+    fn exit_span(&mut self) -> io::Result<()> {
+        let _ = self.reset();
+        Ok(())
+    }
+}
+
+fn render_maybe_with_highlighting(src: String, class: Option<&str>, id: Option<&str>,
+                                  extension: Option<&str>, enclose: bool) -> io::Result {
+    debug!("html highlighting: ================\n{}\n==============", src);
+    let mut out = Vec::new();
+
+    if enclose {
+        write!(out, "
\n", class.unwrap_or(""))?;
+    }
+
+    let sess = parse::ParseSess::new();
+    let fm = sess.codemap().new_filemap("".to_string(), None, src);
+    let mut classifier = Classifier::new(lexer::StringReader::new(&sess, fm), sess.codemap());
+    classifier.write_source(&mut out)?;
+
+    if let Some(extension) = extension {
+        write!(out, "{}", extension)?;
+    }
+
+    if enclose {
+        write!(out, "
\n")?; + } + + Ok(String::from_utf8_lossy(&out[..]).to_string()) } -fn write_header(class: Option<&str>, - id: Option<&str>, - out: &mut Write) - -> io::Result<()> { - write!(out, "
` block.
+pub fn render_inner_with_highlighting(src: &str) -> io::Result {
+    render_maybe_with_highlighting(src.to_string(), None, None, None, false)
+}
+
+/// Highlights `src`, returning the HTML output.
+pub fn render_with_highlighting(src: &str,
+                                class: Option<&str>,
+                                id: Option<&str>,
+                                extension: Option<&str>)
+                                -> String {
+    match render_maybe_with_highlighting(src.to_string(), class, id, extension, true) {
+        Ok(s) => s,
+        Err(_) => src.to_string(),
     }
-    write!(out, "class='rust {}'>\n", class.unwrap_or(""))
 }
 
-fn write_footer(out: &mut Write) -> io::Result<()> {
-    write!(out, "
\n") +/// Highlights `src` and prints it out to stderr. +pub fn render_to_stdout_with_highlighting(src: String) { + debug!("term highlighting: ================\n{}\n==============", src); + let mut t = stdout().unwrap(); + + let sess = parse::ParseSess::new(); + let fm = sess.codemap().new_filemap("".to_string(), None, src); + let mut classifier = Classifier::new(lexer::StringReader::new(&sess, fm), sess.codemap()); + let _ = classifier.write_source(&mut t); } diff --git a/src/librustc_highlight/lib.rs b/src/librustc_highlight/lib.rs new file mode 100644 index 0000000000000..b79b3d6bc6dde --- /dev/null +++ b/src/librustc_highlight/lib.rs @@ -0,0 +1,30 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_name = "rustc_highlight"] +#![unstable(feature = "rustc_highlight", issue = "9999")] +#![crate_type = "dylib"] +#![crate_type = "rlib"] +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/", + html_playground_url = "https://play.rust-lang.org/")] +#![deny(warnings)] + +#![feature(rustc_private)] +#![feature(staged_api)] + +#[macro_use] extern crate syntax; +extern crate syntax_pos; +#[macro_use] extern crate log; +extern crate term; + +pub mod highlight; +pub mod escape; diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 93c0bd6d6d836..534843be396bb 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -21,6 +21,7 @@ rustc_lint = { path = "../librustc_lint" } rustc_metadata = { path = "../librustc_metadata" } rustc_resolve = { path = "../librustc_resolve" } rustc_trans = { path = "../librustc_trans" } +rustc_highlight = { path = "../librustc_highlight" } serialize = { path = "../libserialize" } syntax = { path = "../libsyntax" } syntax_pos = { path = "../libsyntax_pos" } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 503ef4c3183d2..60797b226d24f 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -27,6 +27,7 @@ #![feature(staged_api)] #![feature(test)] #![feature(unicode)] +#![feature(rustc_highlight)] extern crate arena; extern crate getopts; @@ -40,6 +41,7 @@ extern crate rustc_resolve; extern crate rustc_lint; extern crate rustc_back; extern crate rustc_metadata; +extern crate rustc_highlight; extern crate serialize; #[macro_use] extern crate syntax; extern crate syntax_pos; @@ -73,8 +75,8 @@ pub mod core; pub mod doctree; pub mod fold; pub mod html { - pub mod highlight; - pub mod escape; + pub use rustc_highlight::highlight; + pub use rustc_highlight::escape; pub mod item_type; pub mod format; pub mod layout;