5use syntect::parsing::SyntaxReference;
8 assets::HighlightingAssets,
9 config::{Config, VisibleLines},
10 controller::Controller,
13 line_range::{HighlightedLineRanges, LineRange, LineRanges},
14 style::{StyleComponent, StyleComponents},
15 SyntaxMapping, WrappingMode,
18#[cfg(feature = "paging")]
19use crate::paging::PagingMode;
22struct ActiveStyleComponents {
24 vcs_modification_markers: bool,
31pub struct PrettyPrinter<'a> {
32 inputs: Vec<Input<'a>>,
34 assets: HighlightingAssets,
36 highlighted_lines: Vec<LineRange>,
37 term_width: Option<usize>,
38 active_style_components: ActiveStyleComponents,
41impl<'a> PrettyPrinter<'a> {
42 pub fn new() -> Self {
43 let mut config = Config::default();
45 config.colored_output = true;
46 config.true_color = true;
51 assets: HighlightingAssets::from_binary(),
53 highlighted_lines: vec![],
55 active_style_components: ActiveStyleComponents::default(),
59 /// Add an input which should be pretty-printed
60 pub fn input(&mut self, input: Input<'a>) -> &mut Self {
61 self.inputs.push(input);
65 /// Adds multiple inputs which should be pretty-printed
66 pub fn inputs(&mut self, inputs: impl IntoIterator<Item = Input<'a>>) -> &mut Self {
68 self.inputs.push(input);
73 /// Add a file which should be pretty-printed
74 pub fn input_file(&mut self, path: impl AsRef<OsStr>) -> &mut Self {
75 self.input(Input::from_file(path).kind("File"))
78 /// Add multiple files which should be pretty-printed
79 pub fn input_files<I, P>(&mut self, paths: I) -> &mut Self
81 I: IntoIterator<Item = P>,
84 self.inputs(paths.into_iter().map(Input::from_file))
87 /// Add STDIN as an input
88 pub fn input_stdin(&mut self) -> &mut Self {
89 self.inputs.push(Input::from_stdin());
93 /// Add a byte string as an input
94 pub fn input_from_bytes(&mut self, content: &'a [u8]) -> &mut Self {
95 self.input_from_reader(content)
98 /// Add a custom reader as an input
99 pub fn input_from_reader<R: Read + 'a>(&mut self, reader: R) -> &mut Self {
100 self.inputs.push(Input::from_reader(reader));
104 /// Specify the syntax file which should be used (default: auto-detect)
105 pub fn language(&mut self, language: &'a str) -> &mut Self {
106 self.config.language = Some(language);
110 /// The character width of the terminal (default: autodetect)
111 pub fn term_width(&mut self, width: usize) -> &mut Self {
112 self.term_width = Some(width);
116 /// The width of tab characters (default: None - do not turn tabs to spaces)
117 pub fn tab_width(&mut self, tab_width: Option<usize>) -> &mut Self {
118 self.config.tab_width = tab_width.unwrap_or(0);
122 /// Whether or not the output should be colorized (default: true)
123 pub fn colored_output(&mut self, yes: bool) -> &mut Self {
124 self.config.colored_output = yes;
128 /// Whether or not to output 24bit colors (default: true)
129 pub fn true_color(&mut self, yes: bool) -> &mut Self {
130 self.config.true_color = yes;
134 /// Whether to show a header with the file name
135 pub fn header(&mut self, yes: bool) -> &mut Self {
136 self.active_style_components.header = yes;
140 /// Whether to show line numbers
141 pub fn line_numbers(&mut self, yes: bool) -> &mut Self {
142 self.active_style_components.line_numbers = yes;
146 /// Whether to paint a grid, separating line numbers, git changes and the code
147 pub fn grid(&mut self, yes: bool) -> &mut Self {
148 self.active_style_components.grid = yes;
152 /// Whether to paint a horizontal rule to delimit files
153 pub fn rule(&mut self, yes: bool) -> &mut Self {
154 self.active_style_components.rule = yes;
158 /// Whether to show modification markers for VCS changes. This has no effect if
159 /// the `git` feature is not activated.
160 #[cfg(feature = "git")]
161 pub fn vcs_modification_markers(&mut self, yes: bool) -> &mut Self {
162 self.active_style_components.vcs_modification_markers = yes;
166 /// Whether to show "snip" markers between visible line ranges (default: no)
167 pub fn snip(&mut self, yes: bool) -> &mut Self {
168 self.active_style_components.snip = yes;
172 /// Text wrapping mode (default: do not wrap)
173 pub fn wrapping_mode(&mut self, mode: WrappingMode) -> &mut Self {
174 self.config.wrapping_mode = mode;
178 /// Whether or not to use ANSI italics (default: off)
179 pub fn use_italics(&mut self, yes: bool) -> &mut Self {
180 self.config.use_italic_text = yes;
184 /// If and how to use a pager (default: no paging)
185 #[cfg(feature = "paging")]
186 pub fn paging_mode(&mut self, mode: PagingMode) -> &mut Self {
187 self.config.paging_mode = mode;
191 /// Specify the command to start the pager (default: use "less")
192 #[cfg(feature = "paging")]
193 pub fn pager(&mut self, cmd: &'a str) -> &mut Self {
194 self.config.pager = Some(cmd);
198 /// Specify the lines that should be printed (default: all)
199 pub fn line_ranges(&mut self, ranges: LineRanges) -> &mut Self {
200 self.config.visible_lines = VisibleLines::Ranges(ranges);
204 /// Specify a line that should be highlighted (default: none).
205 /// This can be called multiple times to highlight more than one
206 /// line. See also: highlight_range.
207 pub fn highlight(&mut self, line: usize) -> &mut Self {
208 self.highlighted_lines.push(LineRange::new(line, line));
212 /// Specify a range of lines that should be highlighted (default: none).
213 /// This can be called multiple times to highlight more than one range
215 pub fn highlight_range(&mut self, from: usize, to: usize) -> &mut Self {
216 self.highlighted_lines.push(LineRange::new(from, to));
220 /// Specify the highlighting theme
221 pub fn theme(&mut self, theme: impl AsRef<str>) -> &mut Self {
222 self.config.theme = theme.as_ref().to_owned();
226 /// Specify custom file extension / file name to syntax mappings
227 pub fn syntax_mapping(&mut self, mapping: SyntaxMapping<'a>) -> &mut Self {
228 self.config.syntax_mapping = mapping;
232 pub fn themes(&self) -> impl Iterator<Item = &str> {
236 pub fn syntaxes(&self) -> impl Iterator<Item = &SyntaxReference> {
237 self.assets.syntaxes().iter()
240 /// Pretty-print all specified inputs. This method will "use" all stored inputs.
241 /// If you want to call 'print' multiple times, you have to call the appropriate
242 /// input_* methods again.
243 pub fn print(&mut self) -> Result<bool> {
244 self.config.highlighted_lines =
245 HighlightedLineRanges(LineRanges::from(self.highlighted_lines.clone()));
246 self.config.term_width = self
248 .unwrap_or_else(|| Term::stdout().size().1 as usize);
250 let mut style_components = vec![];
251 if self.active_style_components.grid {
252 style_components.push(StyleComponent::Grid);
254 if self.active_style_components.rule {
255 style_components.push(StyleComponent::Rule);
257 if self.active_style_components.header {
258 style_components.push(StyleComponent::Header);
260 if self.active_style_components.line_numbers {
261 style_components.push(StyleComponent::LineNumbers);
263 if self.active_style_components.snip {
264 style_components.push(StyleComponent::Snip);
266 if self.active_style_components.vcs_modification_markers {
267 #[cfg(feature = "git")]
268 style_components.push(StyleComponent::Changes);
270 self.config.style_components = StyleComponents::new(&style_components);
272 // Collect the inputs to print
273 let mut inputs: Vec<Input> = vec![];
274 std::mem::swap(&mut inputs, &mut self.inputs);
276 // Run the controller
277 let controller = Controller::new(&self.config, &self.assets);
278 controller.run(inputs.into_iter().map(|i| i.into()).collect())
282impl Default for PrettyPrinter<'_> {
283 fn default() -> Self {
288/// An input source for the pretty printer.
289pub struct Input<'a> {
290 input: input::Input<'a>,
294 /// A new input from a reader.
295 pub fn from_reader<R: Read + 'a>(reader: R) -> Self {
296 input::Input::from_reader(Box::new(reader)).into()
299 /// A new input from a file.
300 pub fn from_file(path: impl AsRef<OsStr>) -> Self {
301 input::Input::ordinary_file(path.as_ref()).into()
304 /// A new input from bytes.
305 pub fn from_bytes(bytes: &'a [u8]) -> Self {
306 Input::from_reader(bytes)
309 /// A new input from STDIN.
310 pub fn from_stdin() -> Self {
311 input::Input::stdin().into()
314 /// The filename of the input.
315 /// This affects syntax detection and changes the default header title.
316 pub fn name(mut self, name: impl AsRef<OsStr>) -> Self {
317 self.input = self.input.with_name(Some(name.as_ref()));
321 /// The description for the type of input (e.g. "File")
322 pub fn kind(mut self, kind: impl Into<String>) -> Self {
323 let kind = kind.into();
326 .set_kind(if kind.is_empty() { None } else { Some(kind) });
330 /// The title for the input (e.g. "http://example.com/example.txt")
331 /// This defaults to the file name.
332 pub fn title(mut self, title: impl Into<String>) -> Self {
333 self.input.description_mut().set_title(Some(title.into()));
338impl<'a> Into<Input<'a>> for input::Input<'a> {
339 fn into(self) -> Input<'a> {
340 Input { input: self }
344impl<'a> Into<input::Input<'a>> for Input<'a> {
345 fn into(self) -> input::Input<'a> {
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list