1use crate::{Metrics, Position, Span};
64use std::fmt;
65
66#[cfg(feature = "colors")]
68#[derive(Clone, Copy, PartialEq, Eq, Debug)]
69pub enum Color {
70 Red,
71 Green,
72 Blue,
73 Magenta,
74 Yellow,
75 Cyan,
76}
77
78#[cfg(feature = "colors")]
79impl termion::color::Color for Color {
80 fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
81 match self {
82 Self::Red => termion::color::LightRed.write_fg(f),
83 Self::Green => termion::color::LightGreen.write_fg(f),
84 Self::Blue => termion::color::LightBlue.write_fg(f),
85 Self::Magenta => termion::color::LightMagenta.write_fg(f),
86 Self::Yellow => termion::color::LightYellow.write_fg(f),
87 Self::Cyan => termion::color::LightCyan.write_fg(f),
88 }
89 }
90
91 fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
92 match self {
93 Self::Red => termion::color::LightRed.write_bg(f),
94 Self::Green => termion::color::LightGreen.write_bg(f),
95 Self::Blue => termion::color::LightBlue.write_bg(f),
96 Self::Magenta => termion::color::LightMagenta.write_bg(f),
97 Self::Yellow => termion::color::LightYellow.write_bg(f),
98 Self::Cyan => termion::color::LightCyan.write_bg(f),
99 }
100 }
101}
102
103#[cfg(not(feature = "colors"))]
104pub type Color = ();
105
106#[derive(Clone, Copy)]
136pub enum Style {
137 Error,
139
140 Warning,
142
143 Note,
145
146 Help,
148
149 Custom(char, char, Color),
154}
155
156impl Style {
157 #[must_use]
163 #[cfg(not(feature = "colors"))]
164 pub const fn new(underline: char, marker: char) -> Self { Self::Custom(underline, marker, ()) }
165
166 #[must_use]
172 #[cfg(feature = "colors")]
173 pub const fn new(line: char, marker: char, color: Color) -> Self {
174 Self::Custom(line, marker, color)
175 }
176
177 #[must_use]
179 pub fn line(&self) -> char {
180 match self {
181 Self::Error | Self::Warning => '^',
182 Self::Note | Self::Help => '-',
183 Self::Custom(line, _, _) => *line,
184 }
185 }
186
187 #[must_use]
190 pub fn marker(&self) -> char {
191 match self {
192 Self::Error | Self::Warning => '^',
193 Self::Note | Self::Help => '-',
194 Self::Custom(_, marker, _) => *marker,
195 }
196 }
197
198 #[must_use]
200 pub fn color(&self) -> Color {
201 #[cfg(not(feature = "colors"))]
202 {
203 ()
204 }
205 #[cfg(feature = "colors")]
206 {
207 match self {
208 Self::Error => Color::Red,
209 Self::Warning => Color::Yellow,
210 Self::Note => Color::Blue,
211 Self::Help => Color::Green,
212 Self::Custom(_, _, color) => *color,
213 }
214 }
215 }
216}
217
218pub struct Highlight {
287 span: Span,
288 label: Option<String>,
289 style: Style,
290}
291
292impl Highlight {
293 fn margin_nest_level(&self, highlights: &[MappedHighlight]) -> usize {
315 if self.span.line_count() > 1 {
316 let mut level = 2;
317 for h in highlights {
318 if self.span.overlaps(h.span()) {
319 level = std::cmp::max(level, 2 + h.margin_nest_level)
320 }
321 }
322
323 level
324 } else {
325 0
326 }
327 }
328
329 fn start_nest_level(
330 &self,
331 highlights: &[MappedHighlight],
332 first_non_whitespace: Option<usize>,
333 ) -> usize {
334 if self.span.last.line > self.span.start.line
335 && first_non_whitespace.is_some()
336 && first_non_whitespace.unwrap() >= self.span.start.column
337 {
338 0
339 } else {
340 let mut level = 1;
341 for h in highlights {
342 if (self.span.start.line == h.span().start.line
343 || self.span.start.line == h.span().last.line)
344 && (self.span.overlaps(h.span()) || self.span.line_count() > 1)
345 {
346 level = std::cmp::max(level, 1 + h.start_nest_level)
347 }
348 }
349
350 level
351 }
352 }
353
354 fn end_nest_level(&self, highlights: &[MappedHighlight]) -> usize {
355 let mut level = 1;
356 for h in highlights {
357 if (self.span.last.line == h.span().start.line
358 || self.span.last.line == h.span().last.line)
359 && self.span.overlaps(h.span())
360 {
361 level = std::cmp::max(level, 1 + h.end_nest_level)
362 }
363 }
364
365 level
366 }
367}
368
369pub struct Formatter {
386 highlights: Vec<Highlight>,
387 margin_color: Color,
388 show_line_numbers: bool,
389 use_line_begining_shortcut: bool,
390 viewbox: Option<usize>,
391}
392
393impl Formatter {
394 #[must_use]
401 pub fn new() -> Self { Self::default() }
402
403 #[must_use]
411 pub const fn with_margin_color(margin_color: Color) -> Self {
412 Self {
413 highlights: Vec::new(),
414 margin_color,
415 viewbox: Some(2),
416 show_line_numbers: true,
417 use_line_begining_shortcut: true,
418 }
419 }
420
421 pub fn set_line_numbers_visible(&mut self, visible: bool) { self.show_line_numbers = visible; }
441
442 pub fn show_line_numbers(&mut self) { self.show_line_numbers = false; }
444
445 pub fn hide_line_numbers(&mut self) { self.show_line_numbers = false; }
447
448 pub fn set_viewbox(&mut self, viewbox: Option<usize>) { self.viewbox = viewbox }
485
486 pub fn add(&mut self, span: Span, label: Option<String>, style: Style) {
488 self.highlights.push(Highlight { span, label, style });
489 self.highlights.sort_by(|a, b| a.span.cmp(&b.span));
490 }
491
492 #[must_use]
494 pub fn span(&self) -> Option<Span> {
495 let mut span: Option<Span> = None;
496
497 for h in &self.highlights {
498 span = Some(span.map(|s| s.union(h.span)).unwrap_or(h.span))
499 }
500
501 span
502 }
503}
504
505#[derive(Clone, Copy)]
507struct MappedHighlight<'a> {
508 h: &'a Highlight,
509 margin_nest_level: usize,
510 start_nest_level: usize,
511 end_nest_level: usize,
512}
513
514impl<'a> MappedHighlight<'a> {
515 pub const fn span(&self) -> &Span { &self.h.span }
516
517 pub const fn style(&self) -> &Style { &self.h.style }
518
519 pub const fn label(&self) -> Option<&String> { self.h.label.as_ref() }
520
521 fn update_start_nest_level(
522 &mut self,
523 highlights: &[MappedHighlight],
524 first_non_whitespace: Option<usize>,
525 ) {
526 self.start_nest_level = self.h.start_nest_level(highlights, first_non_whitespace)
527 }
528
529 fn update_end_nest_level(&mut self, highlights: &[MappedHighlight]) {
530 self.end_nest_level = self.h.end_nest_level(highlights)
531 }
532}
533
534#[derive(Clone, Copy)]
536pub enum Char {
537 Empty,
538 Text(char),
539 Margin(char, Color),
540 Label(char, Color),
541 SpanMarker(char, Color),
542 SpanUnderline(char, Color),
543 SpanVertical(Color),
544 SpanHorizontal(Color),
545 SpanMargin(Color),
546 SpanMarginMarker(Color),
547}
548
549impl Char {
550 const fn unwrap(self) -> char {
551 match self {
552 Self::Empty => ' ',
553 Self::Text(c)
554 | Self::Margin(c, _)
555 | Self::Label(c, _)
556 | Self::SpanUnderline(c, _)
557 | Self::SpanMarker(c, _) => c,
558 Self::SpanVertical(_) => '|',
559 Self::SpanHorizontal(_) => '_',
560 Self::SpanMargin(_) => '|',
561 Self::SpanMarginMarker(_) => '/',
562 }
563 }
564
565 #[cfg(feature = "colors")]
566 const fn color(&self) -> Option<Color> {
567 match self {
568 Self::Empty | Self::Text(_) => None,
569 Self::Margin(_, color)
570 | Self::Label(_, color)
571 | Self::SpanUnderline(_, color)
572 | Self::SpanMarker(_, color)
573 | Self::SpanVertical(color)
574 | Self::SpanHorizontal(color)
575 | Self::SpanMargin(color) | Self::SpanMarginMarker(color) => Some(*color),
576 }
577 }
578
579 const fn is_free(&self) -> bool {
580 match self {
581 Self::Empty => true,
582 _ => false,
583 }
584 }
585
586 #[allow(clippy::trivially_copy_pass_by_ref)]
587 const fn is_span_horizontal(&self) -> bool {
588 match self {
589 Self::SpanHorizontal(_) => true,
590 _ => false,
591 }
592 }
593
594 #[allow(clippy::trivially_copy_pass_by_ref)]
595 const fn is_span_margin(&self) -> bool {
596 match self {
597 Self::SpanMargin(_) => true,
598 _ => false,
599 }
600 }
601}
602
603impl From<char> for Char {
604 fn from(c: char) -> Self { Self::Text(c) }
605}
606
607struct CharMap {
609 data: Vec<Char>,
610 width: usize,
611 height: usize,
612}
613
614impl CharMap {
615 fn new() -> CharMap {
616 CharMap {
617 data: vec![Char::Empty],
618 width: 1,
619 height: 1,
620 }
621 }
622
623 fn from_label<M: Metrics>(text: &str, color: Color, metrics: &M) -> CharMap {
624 let mut map = CharMap {
625 data: Vec::with_capacity(text.len()),
626 width: 0,
627 height: 0,
628 };
629
630 let mut pos = Position::new(0, 0);
631 for c in text.chars() {
632 match c {
633 '\n' | '\t' => (),
634 _ => map.set(pos.column, pos.line, Char::Label(c, color)),
635 }
636
637 pos.shift(c, metrics)
638 }
639
640 map
641 }
642
643 fn height(&self) -> usize { self.height }
646
647 fn align<I: Iterator<Item = usize>>(&mut self, width: usize, _height: usize, it: I) {
648 for i in it {
649 let x = i % width;
650 let y = i / width;
651
652 if x < self.width {
653 if y < self.height {
654 let j = x + y * self.width;
655 self.data[i] = self.data[j];
656 } else {
657 let my = self.height - 1;
658 self.data[i] = match (self.get(x, my), self.get(x + 1, my)) {
659 (Char::SpanMargin(_), Char::SpanHorizontal(_))
660 if x == 0 || !self.get(x - 1, my).is_span_horizontal() =>
661 {
662 Char::Empty
663 }
664 (Char::SpanMargin(c), _) => Char::SpanMargin(c),
665 (Char::SpanMarginMarker(c), _) => Char::SpanMargin(c),
666 (Char::Empty, Char::SpanHorizontal(c)) => Char::SpanMargin(c),
667 (Char::Margin('|', c), _) => Char::Margin('|', c),
668 _ => Char::Empty,
669 }
670 }
671 } else {
672 self.data[i] = Char::Empty
673 }
674 }
675 }
676
677 fn resize(&mut self, width: usize, height: usize) {
678 let len = width * height;
679
680 if len != self.data.len() {
681 if len > self.data.len() {
682 self.data.resize(len, Char::Empty);
683 }
684
685 if width < self.width {
686 self.align(width, height, 0..len);
687 } else {
688 self.align(width, height, (0..len).rev());
689 }
690
691 if len < self.data.len() {
692 self.data.resize(len, Char::Empty);
693 }
694
695 self.width = width;
696 self.height = height;
697 }
698 }
699
700 fn reserve(&mut self, width: usize, height: usize) {
701 self.resize(
702 std::cmp::max(width, self.width),
703 std::cmp::max(height, self.height),
704 )
705 }
706
707 fn get(&self, x: usize, y: usize) -> Char {
708 if x >= self.width || y >= self.height {
709 Char::Empty
710 } else {
711 self.data[x + y * self.width]
712 }
713 }
714
715 fn set(&mut self, x: usize, y: usize, c: Char) {
716 self.reserve(x + 1, y + 1);
717 self.data[x + y * self.width] = c;
718 }
719
720 fn draw_marker(&mut self, style: &Style, y: usize, x: usize) {
721 let mut head = false;
722 for j in 1..=y {
723 let previous_c = self.get(x, j);
724 if previous_c.is_free() || previous_c.is_span_horizontal() {
725 let c = if head {
726 Char::SpanVertical(style.color())
727 } else {
728 head = true;
729 Char::SpanMarker(style.marker(), style.color())
730 };
731
732 self.set(x, j, c);
733 }
734 }
735 }
736
737 fn draw_open_line(&mut self, style: &Style, y: usize, start: usize, end: usize) {
738 self.reserve(end + 1, y + 1);
739 for x in start..=end {
740 if x == end {
741 self.draw_marker(style, y, x)
742 } else {
743 if !self.get(x, y).is_span_margin() {
744 self.set(x, y, Char::SpanHorizontal(style.color()))
745 }
746 }
747 }
748 }
749
750 fn draw_closed_line(&mut self, style: &Style, y: usize, start: usize, end: usize) {
751 self.reserve(end + 1, y + 1);
752 for x in start..=end {
753 if x == start || x == end {
754 self.draw_marker(style, y, x)
755 } else {
756 let c = if y == 1 {
757 Char::SpanUnderline(style.line(), style.color())
758 } else {
759 Char::SpanHorizontal(style.color())
760 };
761
762 self.set(x, y, c)
763 }
764 }
765 }
766
767 fn is_rect_free(&self, offset_x: usize, offset_y: usize, width: usize, height: usize) -> bool {
769 for y in offset_y..(offset_y + height) {
770 for x in offset_x..(offset_x + width) {
771 if !self.get(x, y).is_free() {
772 return false;
773 }
774 }
775 }
776
777 true
778 }
779
780 fn draw_charmap(&mut self, offset_x: usize, offset_y: usize, map: &CharMap) {
781 self.reserve(offset_x + map.width, offset_y + map.height);
782 for y in 0..map.height {
783 for x in 0..map.width {
784 self.set(offset_x + x, offset_y + y, map.get(x, y))
785 }
786 }
787 }
788
789 fn draw_charmap_if_free(&mut self, offset_x: usize, offset_y: usize, map: &CharMap) -> bool {
790 let mut dx = 0;
791 let mut dy = 0;
792
793 if offset_x > 0 {
794 dx = 1;
795 }
796
797 if offset_y > 1 {
798 dy = 1;
799 }
800
801 if self.is_rect_free(
802 offset_x - dx,
803 offset_y - dy,
804 map.width + dx + 1,
805 map.height + dy + 1,
806 ) {
807 self.draw_charmap(offset_x, offset_y, map);
808 true
809 } else {
810 false
811 }
812 }
813}
814
815impl fmt::Display for CharMap {
816 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
817 #[cfg(feature = "colors")]
818 let mut current_color = None;
819 for y in 0..self.height {
820 for x in 0..self.width {
821 let i = x + y * self.width;
822 let c = self.data[i];
823 #[cfg(feature = "colors")]
824 {
825 if c.color() != current_color && !c.is_free() {
826 current_color = c.color();
827 if let Some(color) = current_color {
828 write!(f, "{}{}", termion::style::Bold, termion::color::Fg(color))?;
829 } else {
830 write!(f, "{}", termion::style::Reset)?;
831 }
832 }
833 }
834 c.unwrap().fmt(f)?;
835 }
836 write!(f, "\n")?;
837 }
838
839 #[cfg(feature = "colors")]
840 write!(f, "{}", termion::style::Reset)?;
841
842 Ok(())
843 }
844}
845
846pub struct Formatted(Vec<CharMap>);
851
852impl fmt::Display for Formatted {
853 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
854 for map in &self.0 {
855 map.fmt(f)?;
856 }
857
858 Ok(())
859 }
860}
861
862pub enum ImportantLines {
864 All,
865 Lines(Vec<usize>, usize),
866}
867
868impl ImportantLines {
869 fn includes(&self, line: usize) -> bool {
870 match self {
871 ImportantLines::All => true,
872 ImportantLines::Lines(important_lines, viewbox) => important_lines
873 .binary_search_by(|candidate| {
874 use std::cmp::Ordering;
875 if line <= candidate + viewbox
876 && line >= candidate - std::cmp::min(candidate, viewbox)
877 {
878 Ordering::Equal
879 } else if line <= candidate + viewbox {
880 Ordering::Greater
881 } else {
882 Ordering::Less
883 }
884 })
885 .is_ok(),
886 }
887 }
888}
889
890impl Formatter {
891 fn important_lines(&self) -> ImportantLines {
892 if let Some(viewbox) = self.viewbox {
893 let mut important_lines = Vec::new();
894 for h in &self.highlights {
895 important_lines.push(h.span.start.line);
896 if h.span.start.line != h.span.last.line {
897 important_lines.push(h.span.last.line)
898 }
899 }
900
901 important_lines.sort_unstable();
902 ImportantLines::Lines(important_lines, viewbox)
903 } else {
904 ImportantLines::All
905 }
906 }
907
908 #[must_use]
912 pub fn margin_len(&self, span: &Span) -> usize {
913 if self.show_line_numbers {
914 let last_line = match self.viewbox {
915 Some(viewbox) => {
916 if let Some(last_highlight) = self.highlights.last() {
917 last_highlight.span.last().line + viewbox
918 } else {
919 return 0
920 }
921 },
922 None => span.last().line
923 };
924
925 (((last_line + 1) as f32).log10() as usize) + 4
926 } else {
927 0
928 }
929 }
930
931 pub fn render<E, I: Iterator<Item = Result<char, E>>, M: Metrics>(
951 &self,
952 input: I,
953 span: Span,
954 metrics: &M,
955 ) -> Result<Formatted, E> {
956 let mut mapped_highlights = Vec::with_capacity(self.highlights.len());
957 let mut nest_margin = 0;
958 for h in &self.highlights {
959 let margin_nest_level = h.margin_nest_level(&mapped_highlights);
960 if margin_nest_level > nest_margin {
964 nest_margin = margin_nest_level;
965 }
966
967 mapped_highlights.push(MappedHighlight {
968 h,
969 margin_nest_level,
970 start_nest_level: 0,
971 end_nest_level: 0,
972 });
973 }
974
975 let margin_len = self.margin_len(&span);
976 let margin = margin_len + nest_margin;
977
978 let mut pos = span.start();
979 let mut lines = vec![CharMap::new()];
980 let important_lines = self.important_lines();
981 let mut is_important_line = important_lines.includes(pos.line);
982 if is_important_line {
983 lines.push(CharMap::new())
984 }
985 let mut first_non_whitespace = None;
986 for c in input {
987 if pos > span.last() {
988 break;
989 }
990
991 let c = c?;
992 let x = margin + pos.column;
993
994 match c {
995 '\n' => {
996 if is_important_line {
997 let line_charmap = lines.last_mut().unwrap();
998 self.draw_line_number(Some(pos.line), line_charmap, margin_len);
999 self.draw_line_highlights(
1000 pos.line,
1001 line_charmap,
1002 margin,
1003 &mut mapped_highlights,
1004 metrics,
1005 first_non_whitespace,
1006 );
1007 }
1008 first_non_whitespace = None;
1009 if important_lines.includes(pos.line + 1) {
1010 if !is_important_line && !lines.is_empty() {
1011 let mut viewbox_charmap = CharMap::new();
1012 self.draw_line_number(None, &mut viewbox_charmap, margin_len);
1013 self.draw_line_highlights(
1014 pos.line,
1015 &mut viewbox_charmap,
1016 margin,
1017 &mut mapped_highlights,
1018 metrics,
1019 None,
1020 );
1021 lines.push(viewbox_charmap)
1022 }
1023 is_important_line = true
1024 } else {
1025 is_important_line = false
1026 }
1027
1028 if is_important_line {
1029 lines.push(CharMap::new())
1030 }
1031 }
1032 '\t' => (),
1033 _ => {
1034 if is_important_line {
1035 if self.use_line_begining_shortcut
1036 && first_non_whitespace.is_none()
1037 && !c.is_whitespace() && !c.is_control()
1038 {
1039 first_non_whitespace = Some(pos.column)
1040 }
1041
1042 lines.last_mut().unwrap().set(x, 0, Char::Text(c))
1043 }
1044 }
1045 }
1046
1047 pos.shift(c, metrics)
1048 }
1049
1050 if is_important_line {
1051 let line_charmap = lines.last_mut().unwrap();
1052 self.draw_line_number(Some(pos.line), line_charmap, margin_len);
1053 self.draw_line_highlights(
1054 pos.line,
1055 line_charmap,
1056 margin,
1057 &mut mapped_highlights,
1058 metrics,
1059 first_non_whitespace,
1060 );
1061 }
1062
1063 Ok(Formatted(lines))
1064 }
1065
1066 fn draw_line_number(
1067 &self,
1068 line: Option<usize>,
1069 charmap: &mut CharMap,
1070 margin_len: usize,
1071 ) {
1072 if margin_len > 0 {
1073 charmap.set(
1074 margin_len - 2,
1075 0,
1076 Char::Margin('|', self.margin_color),
1077 );
1078 match line {
1079 Some(mut line) => {
1080 let mut x = margin_len - 3;
1081 line += 1;
1082
1083 while line > 0 {
1084 x -= 1;
1085 let d = line % 10;
1086
1087 charmap.set(
1088 x,
1089 0,
1090 Char::Margin(
1091 std::char::from_digit(d as u32, 10).unwrap(),
1092 self.margin_color,
1093 ),
1094 );
1095
1096 line /= 10;
1097 }
1098 }
1099 None => {
1100 for x in 0..(margin_len - 3) {
1101 charmap.set(x, 0, Char::Margin('.', self.margin_color))
1102 }
1103 }
1104 }
1105 }
1106 }
1107
1108 fn draw_line_highlights<M: Metrics>(
1109 &self,
1110 line: usize,
1111 charmap: &mut CharMap,
1112 margin: usize,
1113 highlights: &mut [MappedHighlight],
1114 metrics: &M,
1115 first_non_whitespace: Option<usize>,
1116 ) {
1117 for i in 0..highlights.len() {
1119 let mut h = highlights[i];
1120
1121 let mut shortcut = false;
1122 if h.span().start.line == line {
1123 h.update_start_nest_level(&highlights[0..i], first_non_whitespace);
1124
1125 if h.span().last.line == line {
1126 charmap.draw_closed_line(
1127 h.style(),
1128 h.start_nest_level,
1129 margin + h.span().start.column,
1130 margin + h.span().last.column,
1131 )
1132 } else {
1133 if first_non_whitespace.is_some()
1134 && h.span().start.column <= first_non_whitespace.unwrap()
1135 {
1136 shortcut = true;
1138 charmap.set(
1139 margin - h.margin_nest_level,
1140 0,
1141 Char::SpanMarginMarker(h.style().color()),
1142 )
1143 } else {
1144 charmap.draw_open_line(
1145 h.style(),
1146 h.start_nest_level,
1147 margin - h.margin_nest_level + 1,
1148 margin + h.span().start.column,
1149 )
1150 }
1151 }
1152 } else if h.span().last.line == line {
1153 h.update_end_nest_level(&highlights[0..i]);
1154 charmap.draw_open_line(
1155 h.style(),
1156 h.end_nest_level,
1157 margin - h.margin_nest_level + 1,
1158 margin + h.span().last.column,
1159 );
1160 }
1163
1164 if shortcut || (h.span().start.line < line && h.span().last.line >= line) {
1165 let end = if h.span().last.line == line {
1166 h.end_nest_level
1167 } else {
1168 charmap.height() - 1
1169 };
1170
1171 let x = margin - h.margin_nest_level;
1172 let offset_y = if shortcut { 1 } else { 0 };
1173
1174 for y in offset_y..=end {
1175 charmap.set(x, y, Char::SpanMargin(h.style().color()))
1176 }
1177 }
1178
1179 highlights[i] = h;
1180 }
1181
1182 for h in highlights.iter().rev() {
1184 if h.span().last.line == line {
1185 if let Some(label) = h.label() {
1186 let label_charmap = CharMap::from_label(&label, h.style().color(), metrics);
1187 let x = margin + h.span().last.column;
1188 let mut y = 1;
1189 if !charmap.draw_charmap_if_free(x + 2, y, &label_charmap) {
1190 y += 2;
1191 while !charmap.draw_charmap_if_free(x, y, &label_charmap) {
1192 y += 1;
1193 }
1194 }
1195
1196 for vy in 2..y {
1197 charmap.set(x, vy, Char::SpanVertical(h.style().color()));
1198 }
1199 }
1200 }
1201 }
1202 }
1203}
1204
1205impl Default for Formatter {
1206 fn default() -> Formatter {
1207 Formatter {
1208 highlights: Vec::new(),
1209 #[cfg(not(feature = "colors"))]
1210 margin_color: (),
1211 #[cfg(feature = "colors")]
1212 margin_color: Color::Blue,
1213 viewbox: Some(2),
1214 show_line_numbers: true,
1215 use_line_begining_shortcut: true,
1216 }
1217 }
1218}