diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 16522a73f56a5..b023ed699dad6 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -30,7 +30,7 @@ use syntax::parse; use syntax::parse::token::InternedString; use syntax::feature_gate::UnstableFeatures; -use errors::{ColorConfig, FatalError, Handler}; +use errors::{ColorConfig, Drawing, EmitterConfig, FatalError, Handler}; use getopts; use std::collections::{BTreeMap, BTreeSet}; @@ -80,13 +80,13 @@ pub enum OutputType { #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ErrorOutputType { - HumanReadable(ColorConfig), + HumanReadable(EmitterConfig), Json, } impl Default for ErrorOutputType { fn default() -> ErrorOutputType { - ErrorOutputType::HumanReadable(ColorConfig::Auto) + ErrorOutputType::HumanReadable(EmitterConfig::default()) } } @@ -1204,11 +1204,18 @@ pub fn rustc_optgroups() -> Vec { opt::opt_s("", "error-format", "How errors and other messages are produced", "human|json"), - opt::opt_s("", "color", "Configure coloring of output: - auto = colorize, if output goes to a tty (default); - always = always colorize output; - never = never colorize output", "auto|always|never"), - + opt::opt_s("", "color", + "Configure coloring of output: + auto = colorize, if output goes to a tty (default); + always = always colorize output; + never = never colorize output", + "auto|always|never"), + opt::opt_s("", "unicode", + "Configure characters used on output: + ascii = ascii safe characters (default) + style1 = unicode + style2 = unicode box drawing characters", + "ascii|style1|style2"), opt::flagopt_ubnr("", "pretty", "Pretty-print the input instead of compiling; valid types are: `normal` (un-annotated source), @@ -1269,26 +1276,43 @@ pub fn build_session_options_and_crate_config(matches: &getopts::Matches) } }; + let drawing = match matches.opt_str("unicode").as_ref().map(|s| &s[..]) { + Some("style1") => Drawing::unicode(), + Some("style2") => Drawing::unicode_2(), + Some("ascii") => Drawing::ascii(), + None => Drawing::ascii(), + Some(arg) => { + early_error(ErrorOutputType::default(), &format!("argument for --unicode must be style1, \ + style2 or ascii (instead was `{}`)", + arg)) + } + }; + + let config = EmitterConfig { + color: color, + drawing: drawing, + }; + // We need the opts_present check because the driver will send us Matches // with only stable options if no unstable options are used. Since error-format // is unstable, it will not be present. We have to use opts_present not // opt_present because the latter will panic. let error_format = if matches.opts_present(&["error-format".to_owned()]) { match matches.opt_str("error-format").as_ref().map(|s| &s[..]) { - Some("human") => ErrorOutputType::HumanReadable(color), + Some("human") => ErrorOutputType::HumanReadable(config), Some("json") => ErrorOutputType::Json, - None => ErrorOutputType::HumanReadable(color), + None => ErrorOutputType::HumanReadable(config), Some(arg) => { - early_error(ErrorOutputType::HumanReadable(color), + early_error(ErrorOutputType::HumanReadable(config), &format!("argument for --error-format must be human or json (instead \ was `{}`)", arg)) } } } else { - ErrorOutputType::HumanReadable(color) + ErrorOutputType::HumanReadable(config) }; let unparsed_crate_types = matches.opt_strs("crate-type"); diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 724b32d2cd715..a17a54bcb56db 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -23,7 +23,7 @@ use mir::transform as mir_pass; use syntax::ast::NodeId; use errors::{self, DiagnosticBuilder}; -use errors::emitter::{Emitter, EmitterWriter}; +use errors::emitter::{Emitter, EmitterConfig, EmitterWriter}; use syntax::json::JsonEmitter; use syntax::feature_gate; use syntax::parse; @@ -528,13 +528,14 @@ pub fn build_session_with_codemap(sopts: config::Options, let treat_err_as_bug = sopts.debugging_opts.treat_err_as_bug; let emitter: Box = match (sopts.error_format, emitter_dest) { - (config::ErrorOutputType::HumanReadable(color_config), None) => { - Box::new(EmitterWriter::stderr(color_config, + (config::ErrorOutputType::HumanReadable(config), None) => { + Box::new(EmitterWriter::stderr(config, Some(codemap.clone()))) } (config::ErrorOutputType::HumanReadable(_), Some(dst)) => { Box::new(EmitterWriter::new(dst, - Some(codemap.clone()))) + Some(codemap.clone()), + EmitterConfig::default())) } (config::ErrorOutputType::Json, None) => { Box::new(JsonEmitter::stderr(Some(registry), codemap.clone())) @@ -707,8 +708,8 @@ unsafe fn configure_llvm(sess: &Session) { pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! { let emitter: Box = match output { - config::ErrorOutputType::HumanReadable(color_config) => { - Box::new(EmitterWriter::stderr(color_config, + config::ErrorOutputType::HumanReadable(config) => { + Box::new(EmitterWriter::stderr(config, None)) } config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()), @@ -720,8 +721,8 @@ pub fn early_error(output: config::ErrorOutputType, msg: &str) -> ! { pub fn early_warn(output: config::ErrorOutputType, msg: &str) { let emitter: Box = match output { - config::ErrorOutputType::HumanReadable(color_config) => { - Box::new(EmitterWriter::stderr(color_config, + config::ErrorOutputType::HumanReadable(config) => { + Box::new(EmitterWriter::stderr(config, None)) } config::ErrorOutputType::Json => Box::new(JsonEmitter::basic()), diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 6551bad3bc92e..cecc270aaafc7 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -142,7 +142,7 @@ pub fn run(run_compiler: F) -> isize Some(sess) => sess.fatal(&abort_msg(err_count)), None => { let emitter = - errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto, None); + errors::emitter::EmitterWriter::stderr(errors::EmitterConfig::default(), None); let handler = errors::Handler::with_emitter(true, false, Box::new(emitter)); handler.emit(&MultiSpan::new(), &abort_msg(err_count), @@ -1081,7 +1081,7 @@ pub fn monitor(f: F) { // Thread panicked without emitting a fatal diagnostic if !value.is::() { let emitter = - Box::new(errors::emitter::EmitterWriter::stderr(errors::ColorConfig::Auto, None)); + Box::new(errors::emitter::EmitterWriter::stderr(errors::EmitterConfig::default(), None)); let handler = errors::Handler::with_emitter(true, false, emitter); // a .span_bug or .bug call has already printed what diff --git a/src/librustc_errors/emitter.rs b/src/librustc_errors/emitter.rs index a307e9b696def..ecb3b16371fe4 100644 --- a/src/librustc_errors/emitter.rs +++ b/src/librustc_errors/emitter.rs @@ -40,6 +40,105 @@ impl Emitter for EmitterWriter { /// maximum number of lines we will print for each error; arbitrary. pub const MAX_HIGHLIGHT_LINES: usize = 6; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct LineStyle { + pub start: Option, + /// The middle char must always be set to Some(char) + pub middle: Option, + pub end: Option, + pub single: Option, +} + +impl LineStyle { + pub fn homogeneous(c: char) -> LineStyle { + LineStyle { + start: Some(c), + middle: Some(c), + end: Some(c), + single: Some(c), + } + } + + pub fn homogeneous_with_start(c: char, start: Option) -> LineStyle { + LineStyle { + start: start, + middle: Some(c), + end: Some(c), + single: Some(c), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Drawing { + pub primary_underline: LineStyle , + pub secondary_underline: LineStyle, + pub vertical_line: LineStyle, +} + +impl Drawing { + pub fn ascii() -> Drawing { + Drawing { + primary_underline: LineStyle::homogeneous('^'), + secondary_underline: LineStyle::homogeneous('-'), + vertical_line: LineStyle::homogeneous_with_start('|', None), + } + } + + pub fn unicode() -> Drawing { + Drawing { + primary_underline: LineStyle { + start: Some('\u{2594}'), // UPPER ONE EIGHTH BLOCK ▔ + middle: Some('\u{2594}'), // UPPER ONE EIGHTH BLOCK ▔ + end: None, + single: Some('⌅'), // '\u{2580}', // UPPER HALF BLOCK ▀ + }, + secondary_underline: LineStyle::homogeneous('‾'), + vertical_line: LineStyle::homogeneous_with_start('⎸', None), + } + } + + pub fn unicode_2() -> Drawing { + Drawing { + primary_underline: LineStyle::homogeneous('\u{25B2}'), // BLACK UP-POINTING TRIANGLE + secondary_underline: LineStyle { + start: Some('\u{2517}'), // BOX DRAWINGS HEAVY UP AND RIGHT ┗ + middle: Some('\u{2501}'), // BOX DRAWINGS HEAVY HORIZONTAL ━ + end: Some('\u{251B}'), // BOX DRAWINGS HEAVY UP AND LEFT ┛ + single: Some('⎵'), // BOX DRAWINGS HEAVY HORIZONTAL ━ + }, + vertical_line: LineStyle { + start: Some('\u{2523}'), // BOX DRAWINGS HEAVY VERTICAL AND RIGHT ┣ + middle: Some('\u{2503}'), // BOX DRAWINGS HEAVY VERTICAL ┃ + end: Some('\u{2503}'), // BOX DRAWINGS HEAVY VERTICAL ┃ + single: None, + }, + } + } +} + +impl Default for Drawing { + fn default() -> Drawing { + //Drawing::ascii() + Drawing::unicode() + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct EmitterConfig { + pub color: ColorConfig, + pub drawing: Drawing, +} + +impl Default for EmitterConfig { + fn default() -> EmitterConfig { + EmitterConfig { + color: ColorConfig::Auto, + drawing: Drawing::default(), + } + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ColorConfig { Auto, @@ -60,6 +159,7 @@ impl ColorConfig { pub struct EmitterWriter { dst: Destination, cm: Option>, + cfg: EmitterConfig, } struct FileWithAnnotatedLines { @@ -83,25 +183,32 @@ macro_rules! println_maybe_styled { } impl EmitterWriter { - pub fn stderr(color_config: ColorConfig, code_map: Option>) -> EmitterWriter { - if color_config.use_color() { + pub fn stderr(config: EmitterConfig, code_map: Option>) -> EmitterWriter { + if config.color.use_color() { let dst = Destination::from_stderr(); EmitterWriter { dst: dst, cm: code_map, + cfg: config, } } else { EmitterWriter { dst: Raw(Box::new(io::stderr())), cm: code_map, + cfg: config, } } } - pub fn new(dst: Box, code_map: Option>) -> EmitterWriter { + pub fn new(dst: Box, + code_map: Option>, + config: EmitterConfig) + -> EmitterWriter + { EmitterWriter { dst: Raw(dst), cm: code_map, + cfg: config, } } @@ -230,23 +337,28 @@ impl EmitterWriter { // Next, create the highlight line. for annotation in &annotations { - for p in annotation.start_col..annotation.end_col { - if annotation.is_primary { - buffer.putc(line_offset + 1, - width_offset + p, - '^', - Style::UnderlinePrimary); - if !annotation.is_minimized { - buffer.set_style(line_offset, width_offset + p, Style::UnderlinePrimary); - } + let l = annotation.end_col - annotation.start_col; + for (i, p) in (annotation.start_col..annotation.end_col).enumerate() { + let (underline, color) = if annotation.is_primary { + (self.cfg.drawing.primary_underline, Style::UnderlinePrimary) } else { - buffer.putc(line_offset + 1, - width_offset + p, - '-', - Style::UnderlineSecondary); - if !annotation.is_minimized { - buffer.set_style(line_offset, width_offset + p, Style::UnderlineSecondary); - } + (self.cfg.drawing.secondary_underline, Style::UnderlineSecondary) + }; + let middle = underline.middle.unwrap(); + buffer.putc(line_offset + 1, + width_offset + p, + if l == 1 { + underline.single.unwrap_or(middle) + } else if i == 0 { + underline.start.unwrap_or(middle) + } else if i == l - 1 { + underline.end.unwrap_or(middle) + } else { + middle + }, + color); + if !annotation.is_minimized { + buffer.set_style(line_offset, width_offset + p, color); } } } @@ -329,18 +441,22 @@ impl EmitterWriter { // For each blank line, draw a `|` at our column. The // text ought to be long enough for this. + let color = if annotation.is_primary { + Style::UnderlinePrimary + } else { + Style::UnderlineSecondary + }; + if let Some(line) = self.cfg.drawing.vertical_line.start { + buffer.putc(line_offset + 1, + width_offset + annotation.start_col, + line, + color); + } for index in 2..blank_lines { - if annotation.is_primary { - buffer.putc(line_offset + index, - width_offset + annotation.start_col, - '|', - Style::UnderlinePrimary); - } else { - buffer.putc(line_offset + index, - width_offset + annotation.start_col, - '|', - Style::UnderlineSecondary); - } + buffer.putc(line_offset + index, + width_offset + annotation.start_col, + self.cfg.drawing.vertical_line.middle.unwrap(), + color); draw_col_separator(buffer, line_offset + index, width_offset - 2); } diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index badee66b83dea..5b6c9465ff1d8 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -36,7 +36,7 @@ extern crate rustc_unicode; extern crate serialize as rustc_serialize; // used by deriving extern crate syntax_pos; -pub use emitter::ColorConfig; +pub use emitter::{ColorConfig, Drawing, EmitterConfig}; use self::Level::*; @@ -227,12 +227,12 @@ pub struct Handler { } impl Handler { - pub fn with_tty_emitter(color_config: ColorConfig, + pub fn with_tty_emitter(config: EmitterConfig, can_emit_warnings: bool, treat_err_as_bug: bool, cm: Option>) -> Handler { - let emitter = Box::new(EmitterWriter::stderr(color_config, cm)); + let emitter = Box::new(EmitterWriter::stderr(config, cm)); Handler::with_emitter(can_emit_warnings, treat_err_as_bug, emitter) } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 12408c7d3c95b..91512cb2dcd8d 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -13,7 +13,7 @@ use ast::{self, CrateConfig}; use codemap::CodeMap; use syntax_pos::{self, Span, FileMap}; -use errors::{Handler, ColorConfig, DiagnosticBuilder}; +use errors::{Handler, EmitterConfig, DiagnosticBuilder}; use feature_gate::UnstableFeatures; use parse::parser::Parser; use parse::token::InternedString; @@ -53,7 +53,7 @@ pub struct ParseSess { impl ParseSess { pub fn new() -> Self { let cm = Rc::new(CodeMap::new()); - let handler = Handler::with_tty_emitter(ColorConfig::Auto, + let handler = Handler::with_tty_emitter(EmitterConfig::default(), true, false, Some(cm.clone()));