nreplops_tool/pprint/
mod.rs

1// pprint/mod.rs
2// Copyright 2024 Matti Hänninen
3//
4// Licensed under the Apache License, Version 2.0 (the "License"); you may not
5// use this file except in compliance with the License. You may obtain a copy of
6// the License at
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13// License for the specific language governing permissions and limitations under
14// the License.
15
16//
17// So the different representations that the result being printed passes through
18// are the following:
19//
20// 1. unparsed result
21// 2. list of lexemes
22// 3. result value AST
23// 4. input structure to layout solver
24// 5. input structure to printer
25//
26// The unparsed result (1) is what we receive from the nREPL server.  This
27// is produced by Clojure's printer so it is Clojure but, importantly, very
28// restricted form of Clojure.  It does not contain meta data literal and most
29// of the reader macros are absent as well.
30//
31// The lexemes (2) are produced with our Clojure lexer and should be able to
32// represent the whole language.  However, as the input result is restricted
33// form of Clojure, so the produced lexemes are also a subset of known lexemes.
34//
35// The abstract syntax tree (3) is capable of representing only a limited subset
36// of Clojure; this is effectively the EDN.  It is rich enough so that we can
37// formulate the problem for the layout solver and, optionally, translate the
38// data into some other form (e.g. JSON, YAML, or CSV).
39//
40// The input to the pretty-printing layout solver (4) is either a flat list
41// or a tree structure.  I'm not sure which one it will be.  In any case it
42// conssists of chunks of unbreakable texts together with styling information,
43// suggested breakpoints, layout anchors, optional whitespacing, relationships
44// between breakpoints and the like.  Things that the layout solver uses while
45// determining the optimal layout.
46//
47// The input to the printer (5) consists of text fragments, style coding
48// (separated from the text), line breaks, and spacing.  There is not much
49// conditionality at this phase; only the decision whether to include or
50// ignore the styling (coloring) when printing according to the alraedy fixed
51// layout.
52//
53// When we are printing unformatted but colored output we can skip the phases
54// (3) and (4) produce the printer input (5) directly from the lexemes (2).
55//
56// When we are converting the results to some other output format (e.g. JSON,
57// YAML, or CSV) we follow through the same phases but do the translation
58// into the alternative output format when we produce the input for the layout
59// solver.
60//
61
62use std::io::{self, Write};
63
64mod fragments;
65mod layout_solver;
66mod pretty_edn;
67mod printer;
68mod style;
69mod unformatted_edn;
70
71use crate::clojure::{lex::Lexeme, result_ir};
72
73#[derive(Debug)]
74pub struct ClojureResultPrinter {
75  pub pretty: bool,
76  pub color: bool,
77  pub width: u16,
78}
79
80impl ClojureResultPrinter {
81  pub fn new(pretty: bool, color: bool, width: u16) -> Self {
82    Self {
83      pretty,
84      color,
85      width,
86    }
87  }
88
89  pub fn print(
90    &self,
91    writer: &mut impl Write,
92    lexemes: &[Lexeme],
93  ) -> io::Result<()> {
94    let mut printer_input = Vec::new();
95    if self.pretty {
96      let value = result_ir::build(lexemes).unwrap();
97      let chunks = pretty_edn::convert_to_layout_program(&value);
98      layout_solver::solve(&chunks, &mut printer_input);
99    } else {
100      unformatted_edn::generate_printer_input(
101        lexemes.iter(),
102        &mut printer_input,
103      );
104    }
105    printer::print(writer, printer_input.iter(), self.color)?;
106    writeln!(writer)
107  }
108}