|
|
@@ -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("<stdin>".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!("<pre>{}</pre>", 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 `<pre>` block. |
|
|
|
pub fn render_inner_with_highlighting(src: &str) -> io::Result<String> { |
|
|
|
let sess = parse::ParseSess::new(); |
|
|
|
let fm = sess.codemap().new_filemap("<stdin>".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<T: Display>(&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<U: Write> Writer for U { |
|
|
|
fn string<T: Display>(&mut self, |
|
|
|
text: T, |
|
|
|
klass: Class, |
|
|
|
_tas: Option<&TokenAndSpan>) |
|
|
|
-> io::Result<()> { |
|
|
|
match klass { |
|
|
|
Class::None => write!(self, "{}", text), |
|
|
|
klass => write!(self, "<span class='{}'>{}</span>", klass.rustdoc_class(), text), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn enter_span(&mut self, klass: Class) -> io::Result<()> { |
|
|
|
write!(self, "<span class='{}'>", klass.rustdoc_class()) |
|
|
|
} |
|
|
|
|
|
|
|
fn exit_span(&mut self) -> io::Result<()> { |
|
|
|
write!(self, "</span>") |
|
|
|
} |
|
|
|
-> 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<W: Writer>(&mut self, |
|
|
|
pub fn write_source<W: Writer + Write>(&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<W: Writer>(&mut self, |
|
|
|
fn write_token<W: Writer + Write>(&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<Attr> { |
|
|
|
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<u8> { |
|
|
|
fn string<T: Display>(&mut self, |
|
|
|
text: T, |
|
|
|
klass: Class, |
|
|
|
_tas: Option<&TokenAndSpan>) |
|
|
|
-> io::Result<()> { |
|
|
|
match klass { |
|
|
|
Class::None => write!(self, "{}", text), |
|
|
|
klass => write!(self, |
|
|
|
"<span class='{}'>{}</span>", |
|
|
|
klass.rustdoc_class(), |
|
|
|
Escape(&format!("{}", text))), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn enter_span(&mut self, klass: Class) -> io::Result<()> { |
|
|
|
write!(self, "<span class='{}'>", klass.rustdoc_class()) |
|
|
|
} |
|
|
|
|
|
|
|
fn exit_span(&mut self) -> io::Result<()> { |
|
|
|
write!(self, "</span>") |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// This implements behaviour for terminal output. |
|
|
|
impl Writer for Box<Terminal<Output=io::Stdout> + Send> { |
|
|
|
fn string<T: Display>(&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<String> { |
|
|
|
debug!("html highlighting: ================\n{}\n==============", src); |
|
|
|
let mut out = Vec::new(); |
|
|
|
|
|
|
|
if enclose { |
|
|
|
write!(out, "<pre ")?; |
|
|
|
if let Some(id) = id { |
|
|
|
write!(out, "id='{}' ", id)?; |
|
|
|
} |
|
|
|
write!(out, "class='rust {}'>\n", class.unwrap_or(""))?; |
|
|
|
} |
|
|
|
|
|
|
|
let sess = parse::ParseSess::new(); |
|
|
|
let fm = sess.codemap().new_filemap("<stdin>".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, "</pre>\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, "<pre ")?; |
|
|
|
if let Some(id) = id { |
|
|
|
write!(out, "id='{}' ", id)?; |
|
|
|
/// 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 `<pre>` block. |
|
|
|
pub fn render_inner_with_highlighting(src: &str) -> io::Result<String> { |
|
|
|
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, "</pre>\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("<stdin>".to_string(), None, src); |
|
|
|
let mut classifier = Classifier::new(lexer::StringReader::new(&sess, fm), sess.codemap()); |
|
|
|
let _ = classifier.write_source(&mut t); |
|
|
|
} |