1use std::{cmp, fmt, io};
2
3#[cfg(feature = "termcolor")]
4use termcolor::{ColorSpec, WriteColor};
5
6use crate::{Doc, DocPtr};
7
8pub trait Render {
10 type Error;
11
12 fn write_str(&mut self, s: &str) -> Result<usize, Self::Error>;
13
14 fn write_str_all(&mut self, mut s: &str) -> Result<(), Self::Error> {
15 while !s.is_empty() {
16 let count = self.write_str(s)?;
17 s = &s[count..];
18 }
19 Ok(())
20 }
21
22 fn fail_doc(&self) -> Self::Error;
23}
24
25pub struct IoWrite<W> {
27 upstream: W,
28}
29
30impl<W> IoWrite<W> {
31 pub fn new(upstream: W) -> IoWrite<W> {
32 IoWrite { upstream }
33 }
34}
35
36impl<W> Render for IoWrite<W>
37where
38 W: io::Write,
39{
40 type Error = io::Error;
41
42 fn write_str(&mut self, s: &str) -> io::Result<usize> {
43 self.upstream.write(s.as_bytes())
44 }
45
46 fn write_str_all(&mut self, s: &str) -> io::Result<()> {
47 self.upstream.write_all(s.as_bytes())
48 }
49
50 fn fail_doc(&self) -> Self::Error {
51 io::Error::new(io::ErrorKind::Other, "Document failed to render")
52 }
53}
54
55pub struct FmtWrite<W> {
57 upstream: W,
58}
59
60impl<W> FmtWrite<W> {
61 pub fn new(upstream: W) -> FmtWrite<W> {
62 FmtWrite { upstream }
63 }
64}
65
66impl<W> Render for FmtWrite<W>
67where
68 W: fmt::Write,
69{
70 type Error = fmt::Error;
71
72 fn write_str(&mut self, s: &str) -> Result<usize, fmt::Error> {
73 self.write_str_all(s).map(|_| s.len())
74 }
75
76 fn write_str_all(&mut self, s: &str) -> fmt::Result {
77 self.upstream.write_str(s)
78 }
79
80 fn fail_doc(&self) -> Self::Error {
81 fmt::Error
82 }
83}
84
85pub trait RenderAnnotated<'a, A>: Render {
87 fn push_annotation(&mut self, annotation: &'a A) -> Result<(), Self::Error>;
88 fn pop_annotation(&mut self) -> Result<(), Self::Error>;
89}
90
91impl<A, W> RenderAnnotated<'_, A> for IoWrite<W>
92where
93 W: io::Write,
94{
95 fn push_annotation(&mut self, _: &A) -> Result<(), Self::Error> {
96 Ok(())
97 }
98
99 fn pop_annotation(&mut self) -> Result<(), Self::Error> {
100 Ok(())
101 }
102}
103
104impl<A, W> RenderAnnotated<'_, A> for FmtWrite<W>
105where
106 W: fmt::Write,
107{
108 fn push_annotation(&mut self, _: &A) -> Result<(), Self::Error> {
109 Ok(())
110 }
111
112 fn pop_annotation(&mut self) -> Result<(), Self::Error> {
113 Ok(())
114 }
115}
116
117#[cfg(feature = "termcolor")]
118pub struct TermColored<W> {
119 color_stack: Vec<ColorSpec>,
120 upstream: W,
121}
122
123#[cfg(feature = "termcolor")]
124impl<W> TermColored<W> {
125 pub fn new(upstream: W) -> TermColored<W> {
126 TermColored {
127 color_stack: Vec::new(),
128 upstream,
129 }
130 }
131}
132
133#[cfg(feature = "termcolor")]
134impl<W> Render for TermColored<W>
135where
136 W: io::Write,
137{
138 type Error = io::Error;
139
140 fn write_str(&mut self, s: &str) -> io::Result<usize> {
141 self.upstream.write(s.as_bytes())
142 }
143
144 fn write_str_all(&mut self, s: &str) -> io::Result<()> {
145 self.upstream.write_all(s.as_bytes())
146 }
147
148 fn fail_doc(&self) -> Self::Error {
149 io::Error::new(io::ErrorKind::Other, "Document failed to render")
150 }
151}
152
153#[cfg(feature = "termcolor")]
154impl<W> RenderAnnotated<'_, ColorSpec> for TermColored<W>
155where
156 W: WriteColor,
157{
158 fn push_annotation(&mut self, color: &ColorSpec) -> Result<(), Self::Error> {
159 self.color_stack.push(color.clone());
160 self.upstream.set_color(color)
161 }
162
163 fn pop_annotation(&mut self) -> Result<(), Self::Error> {
164 self.color_stack.pop();
165 match self.color_stack.last() {
166 Some(previous) => self.upstream.set_color(previous),
167 None => self.upstream.reset(),
168 }
169 }
170}
171
172enum Annotation<'a, A> {
173 Push(&'a A),
174 Pop,
175}
176
177struct BufferWrite<'a, A> {
178 buffer: String,
179 annotations: Vec<(usize, Annotation<'a, A>)>,
180}
181
182impl<'a, A> BufferWrite<'a, A> {
183 fn new() -> Self {
184 BufferWrite {
185 buffer: String::new(),
186 annotations: Vec::new(),
187 }
188 }
189
190 fn render<W>(&mut self, render: &mut W) -> Result<(), W::Error>
191 where
192 W: RenderAnnotated<'a, A>,
193 W: ?Sized,
194 {
195 let mut start = 0;
196 for (end, annotation) in &self.annotations {
197 let s = &self.buffer[start..*end];
198 if !s.is_empty() {
199 render.write_str_all(s)?;
200 }
201 start = *end;
202 match annotation {
203 Annotation::Push(a) => render.push_annotation(a)?,
204 Annotation::Pop => render.pop_annotation()?,
205 }
206 }
207 let s = &self.buffer[start..];
208 if !s.is_empty() {
209 render.write_str_all(s)?;
210 }
211 Ok(())
212 }
213}
214
215impl<A> Render for BufferWrite<'_, A> {
216 type Error = ();
217
218 fn write_str(&mut self, s: &str) -> Result<usize, Self::Error> {
219 self.buffer.push_str(s);
220 Ok(s.len())
221 }
222
223 fn write_str_all(&mut self, s: &str) -> Result<(), Self::Error> {
224 self.buffer.push_str(s);
225 Ok(())
226 }
227
228 fn fail_doc(&self) -> Self::Error {}
229}
230
231impl<'a, A> RenderAnnotated<'a, A> for BufferWrite<'a, A> {
232 fn push_annotation(&mut self, a: &'a A) -> Result<(), Self::Error> {
233 self.annotations
234 .push((self.buffer.len(), Annotation::Push(a)));
235 Ok(())
236 }
237
238 fn pop_annotation(&mut self) -> Result<(), Self::Error> {
239 self.annotations.push((self.buffer.len(), Annotation::Pop));
240 Ok(())
241 }
242}
243
244macro_rules! make_spaces {
245 () => { "" };
246 ($s: tt $($t: tt)*) => { concat!(" ", make_spaces!($($t)*)) };
247}
248
249pub(crate) const SPACES: &str = make_spaces!(,,,,,,,,,,);
250
251fn append_docs2<'a, 'd, T, A>(
252 ldoc: &'d Doc<'a, T, A>,
253 rdoc: &'d Doc<'a, T, A>,
254 mut consumer: impl FnMut(&'d Doc<'a, T, A>),
255) -> &'d Doc<'a, T, A>
256where
257 T: DocPtr<'a, A>,
258{
259 let d = append_docs(rdoc, &mut consumer);
260 consumer(d);
261 append_docs(ldoc, &mut consumer)
262}
263
264fn append_docs<'a, 'd, T, A>(
265 mut doc: &'d Doc<'a, T, A>,
266 consumer: &mut impl FnMut(&'d Doc<'a, T, A>),
267) -> &'d Doc<'a, T, A>
268where
269 T: DocPtr<'a, A>,
270{
271 loop {
272 match doc {
276 Doc::Append(l, r) => {
277 consumer(r);
278 doc = l;
279 }
280 _ => return doc,
281 }
282 }
283}
284
285pub fn best<'a, W, T, A>(doc: &Doc<'a, T, A>, width: usize, out: &mut W) -> Result<(), W::Error>
286where
287 T: DocPtr<'a, A> + 'a,
288 for<'b> W: RenderAnnotated<'b, A>,
289 W: ?Sized,
290{
291 let temp_arena = &typed_arena::Arena::new();
292 Best {
293 pos: 0,
294 bcmds: vec![(0, Mode::Break, doc)],
295 fcmds: vec![],
296 annotation_levels: vec![],
297 width,
298 temp_arena,
299 }
300 .best(0, out)?;
301
302 Ok(())
303}
304
305#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
306enum Mode {
307 Break,
308 Flat,
309}
310
311type Cmd<'d, 'a, T, A> = (usize, Mode, &'d Doc<'a, T, A>);
312
313fn write_newline<W>(ind: usize, out: &mut W) -> Result<(), W::Error>
314where
315 W: ?Sized + Render,
316{
317 out.write_str_all("\n")?;
318 write_spaces(ind, out)
319}
320
321fn write_spaces<W>(spaces: usize, out: &mut W) -> Result<(), W::Error>
322where
323 W: ?Sized + Render,
324{
325 let mut inserted = 0;
326 while inserted < spaces {
327 let insert = cmp::min(SPACES.len(), spaces - inserted);
328 inserted += out.write_str(&SPACES[..insert])?;
329 }
330
331 Ok(())
332}
333
334struct Best<'d, 'a, T, A>
335where
336 T: DocPtr<'a, A> + 'a,
337{
338 pos: usize,
339 bcmds: Vec<Cmd<'d, 'a, T, A>>,
340 fcmds: Vec<&'d Doc<'a, T, A>>,
341 annotation_levels: Vec<usize>,
342 width: usize,
343 temp_arena: &'d typed_arena::Arena<T>,
344}
345
346impl<'d, 'a, T, A> Best<'d, 'a, T, A>
347where
348 T: DocPtr<'a, A> + 'a,
349{
350 fn fitting(&mut self, next: &'d Doc<'a, T, A>, mut pos: usize, ind: usize) -> bool {
351 let mut bidx = self.bcmds.len();
352 self.fcmds.clear(); self.fcmds.push(next);
354
355 let mut mode = Mode::Flat;
356 loop {
357 let mut doc = match self.fcmds.pop() {
358 None => {
359 if bidx == 0 {
360 return true;
362 } else {
363 bidx -= 1;
364 mode = Mode::Break;
365 self.bcmds[bidx].2
366 }
367 }
368 Some(cmd) => cmd,
369 };
370
371 loop {
372 match *doc {
373 Doc::Nil => {}
374 Doc::Append(ref ldoc, ref rdoc) => {
375 doc = append_docs2(ldoc, rdoc, |doc| self.fcmds.push(doc));
376 continue;
377 }
378 Doc::Hardline => return mode == Mode::Break,
381 Doc::RenderLen(len, _) => {
382 pos += len;
383 if pos > self.width {
384 return false;
385 }
386 }
387 Doc::BorrowedText(str) => {
388 pos += str.len();
389 if pos > self.width {
390 return false;
391 }
392 }
393 Doc::OwnedText(ref str) => {
394 pos += str.len();
395 if pos > self.width {
396 return false;
397 }
398 }
399 Doc::SmallText(ref str) => {
400 pos += str.len();
401 if pos > self.width {
402 return false;
403 }
404 }
405 Doc::FlatAlt(ref b, ref f) => {
406 doc = match mode {
407 Mode::Break => b,
408 Mode::Flat => f,
409 };
410 continue;
411 }
412
413 Doc::Column(ref f) => {
414 doc = self.temp_arena.alloc(f(pos));
415 continue;
416 }
417 Doc::Nesting(ref f) => {
418 doc = self.temp_arena.alloc(f(ind));
419 continue;
420 }
421 Doc::Nest(_, ref next)
422 | Doc::Group(ref next)
423 | Doc::Annotated(_, ref next)
424 | Doc::Union(_, ref next) => {
425 doc = next;
426 continue;
427 }
428 Doc::Fail => return false,
429 }
430 break;
431 }
432 }
433 }
434
435 fn best<W>(&mut self, top: usize, out: &mut W) -> Result<bool, W::Error>
436 where
437 W: RenderAnnotated<'d, A>,
438 W: ?Sized,
439 {
440 let mut fits = true;
441
442 while top < self.bcmds.len() {
443 let mut cmd = self.bcmds.pop().unwrap();
444 loop {
445 let (ind, mode, doc) = cmd;
446 match *doc {
447 Doc::Nil => {}
448 Doc::Append(ref ldoc, ref rdoc) => {
449 cmd.2 = append_docs2(ldoc, rdoc, |doc| self.bcmds.push((ind, mode, doc)));
450 continue;
451 }
452 Doc::FlatAlt(ref b, ref f) => {
453 cmd.2 = match mode {
454 Mode::Break => b,
455 Mode::Flat => f,
456 };
457 continue;
458 }
459 Doc::Group(ref doc) => {
460 if let Mode::Break = mode {
461 if self.fitting(doc, self.pos, ind) {
462 cmd.1 = Mode::Flat;
463 }
464 }
465 cmd.2 = doc;
466 continue;
467 }
468 Doc::Nest(off, ref doc) => {
469 let new_ind = if off >= 0 {
472 ind.saturating_add(off as usize)
473 } else {
474 ind.saturating_sub(off.unsigned_abs())
475 };
476 cmd = (new_ind, mode, doc);
477 continue;
478 }
479 Doc::Hardline => {
480 if let Some(next) = self.bcmds.pop() {
483 write_newline(next.0, out)?;
484 self.pos = next.0;
485 cmd = next;
486 continue;
487 } else {
488 write_newline(ind, out)?;
489 self.pos = ind;
490 }
491 }
492 Doc::RenderLen(len, ref doc) => match **doc {
493 Doc::OwnedText(ref s) => {
494 out.write_str_all(s)?;
495 self.pos += len;
496 fits &= self.pos <= self.width;
497 }
498 Doc::BorrowedText(s) => {
499 out.write_str_all(s)?;
500 self.pos += len;
501 fits &= self.pos <= self.width;
502 }
503 Doc::SmallText(ref s) => {
504 out.write_str_all(s)?;
505 self.pos += len;
506 fits &= self.pos <= self.width;
507 }
508 _ => unreachable!(),
509 },
510 Doc::OwnedText(ref s) => {
511 out.write_str_all(s)?;
512 self.pos += s.len();
513 fits &= self.pos <= self.width;
514 }
515 Doc::BorrowedText(s) => {
516 out.write_str_all(s)?;
517 self.pos += s.len();
518 fits &= self.pos <= self.width;
519 }
520 Doc::SmallText(ref s) => {
521 out.write_str_all(s)?;
522 self.pos += s.len();
523 fits &= self.pos <= self.width;
524 }
525 Doc::Annotated(ref ann, ref doc) => {
526 out.push_annotation(ann)?;
527 self.annotation_levels.push(self.bcmds.len());
528 cmd.2 = doc;
529 continue;
530 }
531 Doc::Union(ref l, ref r) => {
532 let pos = self.pos;
533 let annotation_levels = self.annotation_levels.len();
534 let bcmds = self.bcmds.len();
535
536 self.bcmds.push((ind, mode, l));
537
538 let mut buffer = BufferWrite::new();
539
540 match self.best(bcmds, &mut buffer) {
541 Ok(true) => buffer.render(out)?,
542 Ok(false) | Err(()) => {
543 self.pos = pos;
544 self.bcmds.truncate(bcmds);
545 self.annotation_levels.truncate(annotation_levels);
546 cmd.2 = r;
547 continue;
548 }
549 }
550 }
551 Doc::Column(ref f) => {
552 cmd.2 = self.temp_arena.alloc(f(self.pos));
553 continue;
554 }
555 Doc::Nesting(ref f) => {
556 cmd.2 = self.temp_arena.alloc(f(ind));
557 continue;
558 }
559 Doc::Fail => return Err(out.fail_doc()),
560 }
561
562 break;
563 }
564 while self.annotation_levels.last() == Some(&self.bcmds.len()) {
565 self.annotation_levels.pop();
566 out.pop_annotation()?;
567 }
568 }
569
570 Ok(fits)
571 }
572}