use pretty.rs for formatting

This commit is contained in:
Folkert 2020-04-04 01:41:17 +02:00
parent 3987ed47dc
commit ba16eff24d
4 changed files with 423 additions and 362 deletions

View file

@ -14,6 +14,7 @@ roc_types = { path = "../types" }
roc_load = { path = "../load" } roc_load = { path = "../load" }
roc_can = { path = "../can" } roc_can = { path = "../can" }
roc_solve = { path = "../solve" } roc_solve = { path = "../solve" }
ven_pretty = { path = "../../vendor/pretty" }
inlinable_string = "0.1.0" inlinable_string = "0.1.0"
[dev-dependencies] [dev-dependencies]

View file

@ -7,69 +7,48 @@ use roc_types::subs::{Content, Subs};
use roc_types::types::{write_error_type, ErrorType}; use roc_types::types::{write_error_type, ErrorType};
use std::path::PathBuf; use std::path::PathBuf;
// use ven_pretty::termcolor::{Color, ColorChoice, ColorSpec, StandardStream};
use std::fmt;
use ven_pretty::{
BoxAllocator, BoxDoc, DocAllocator, DocBuilder, FmtWrite, Render, RenderAnnotated,
};
type Doc<'a> = DocBuilder<'a, BoxAllocator, Annotation>;
/// A textual report. /// A textual report.
pub struct Report { pub struct Report {
pub filename: PathBuf, pub filename: PathBuf,
pub text: ReportText, pub text: ReportText,
} }
pub struct Palette { pub struct Palette<'a> {
pub primary: Color, pub primary: &'a str,
pub code_block: Color, pub code_block: &'a str,
pub variable: Color, pub variable: &'a str,
pub flex_var: Color, pub type_variable: &'a str,
pub rigid_var: Color, pub structure: &'a str,
pub structure: Color, pub alias: &'a str,
pub alias: Color, pub error: &'a str,
pub error: Color, pub line_number: &'a str,
pub line_number: Color, pub gutter_bar: &'a str,
pub gutter_bar: Color, pub module_name: &'a str,
pub module_name: Color, pub binop: &'a str,
pub binop: Color,
}
#[derive(Copy, Clone)]
pub enum Color {
White,
Red,
Blue,
Yellow,
Green,
Cyan,
Magenta,
} }
pub const TEST_PALETTE: Palette = Palette { pub const TEST_PALETTE: Palette = Palette {
primary: Color::White, primary: WHITE_CODE,
code_block: Color::White, code_block: WHITE_CODE,
variable: Color::Blue, variable: BLUE_CODE,
flex_var: Color::Yellow, type_variable: YELLOW_CODE,
rigid_var: Color::Yellow, structure: GREEN_CODE,
structure: Color::Green, alias: YELLOW_CODE,
alias: Color::Yellow, error: RED_CODE,
error: Color::Red, line_number: CYAN_CODE,
line_number: Color::Cyan, gutter_bar: MAGENTA_CODE,
gutter_bar: Color::Magenta, module_name: GREEN_CODE,
module_name: Color::Green, binop: GREEN_CODE,
binop: Color::Green,
}; };
impl Color {
pub fn render(self, str: &str) -> String {
use Color::*;
match self {
Red => red(str),
White => white(str),
Blue => blue(str),
Yellow => yellow(str),
Green => green(str),
Cyan => cyan(str),
Magenta => magenta(str),
}
}
}
pub fn can_problem(filename: PathBuf, problem: Problem) -> Report { pub fn can_problem(filename: PathBuf, problem: Problem) -> Report {
let mut texts = Vec::new(); let mut texts = Vec::new();
@ -247,52 +226,6 @@ pub const BOLD_CODE: &str = "\u{001b}[1m";
pub const UNDERLINE_CODE: &str = "\u{001b}[4m"; pub const UNDERLINE_CODE: &str = "\u{001b}[4m";
fn code(code_str: &str, str: &str) -> String {
let mut buf = String::new();
buf.push_str(code_str);
buf.push_str(str);
buf.push_str(RESET_CODE);
buf
}
pub fn underline(str: &str) -> String {
code(UNDERLINE_CODE, str)
}
pub fn bold(str: &str) -> String {
code(BOLD_CODE, str)
}
fn cyan(str: &str) -> String {
code(CYAN_CODE, str)
}
fn magenta(str: &str) -> String {
code(MAGENTA_CODE, str)
}
fn green(str: &str) -> String {
code(GREEN_CODE, str)
}
fn yellow(str: &str) -> String {
code(YELLOW_CODE, str)
}
fn blue(str: &str) -> String {
code(BLUE_CODE, str)
}
fn red(str: &str) -> String {
code(RED_CODE, str)
}
fn white(str: &str) -> String {
code(WHITE_CODE, str)
}
pub const RESET_CODE: &str = "\u{001b}[0m"; pub const RESET_CODE: &str = "\u{001b}[0m";
struct CiEnv<'a> { struct CiEnv<'a> {
@ -301,6 +234,207 @@ struct CiEnv<'a> {
interns: &'a Interns, interns: &'a Interns,
} }
pub struct CiWrite<W> {
style_stack: Vec<Annotation>,
upstream: W,
}
impl<W> CiWrite<W> {
pub fn new(upstream: W) -> CiWrite<W> {
CiWrite {
style_stack: vec![],
upstream,
}
}
}
pub struct ColorWrite<'a, W> {
style_stack: Vec<Annotation>,
palette: &'a Palette<'a>,
upstream: W,
}
impl<'a, W> ColorWrite<'a, W> {
pub fn new(palette: &'a Palette, upstream: W) -> ColorWrite<'a, W> {
ColorWrite {
style_stack: vec![],
palette,
upstream,
}
}
}
#[derive(Copy, Clone)]
pub enum Annotation {
Emphasized,
Url,
Keyword,
GlobalTag,
PrivateTag,
RecordField,
TypeVariable,
Alias,
Structure,
Symbol,
BinOp,
Error,
GutterBar,
LineNumber,
PlainText,
CodeBlock,
Module,
}
impl<W> Render for CiWrite<W>
where
W: fmt::Write,
{
type Error = fmt::Error;
fn write_str(&mut self, s: &str) -> Result<usize, fmt::Error> {
self.write_str_all(s).map(|_| s.len())
}
fn write_str_all(&mut self, s: &str) -> fmt::Result {
self.upstream.write_str(s)
}
}
impl<W> RenderAnnotated<Annotation> for CiWrite<W>
where
W: fmt::Write,
{
fn push_annotation(&mut self, annotation: &Annotation) -> Result<(), Self::Error> {
use Annotation::*;
match annotation {
Emphasized => {
self.write_str("*")?;
}
Url => {
self.write_str("<")?;
}
GlobalTag | PrivateTag | RecordField | Keyword => {
self.write_str("`")?;
}
CodeBlock | PlainText | LineNumber | Error | GutterBar | TypeVariable | Alias
| Module | Structure | Symbol | BinOp => {}
}
self.style_stack.push(*annotation);
Ok(())
}
fn pop_annotation(&mut self) -> Result<(), Self::Error> {
use Annotation::*;
match self.style_stack.pop() {
None => {}
Some(annotation) => match annotation {
Emphasized => {
self.write_str("*")?;
}
Url => {
self.write_str(">")?;
}
GlobalTag | PrivateTag | RecordField | Keyword => {
self.write_str("`")?;
}
CodeBlock | PlainText | LineNumber | Error | GutterBar | TypeVariable | Alias
| Module | Structure | Symbol | BinOp => {}
},
}
Ok(())
}
}
impl<'a, W> Render for ColorWrite<'a, W>
where
W: fmt::Write,
{
type Error = fmt::Error;
fn write_str(&mut self, s: &str) -> Result<usize, fmt::Error> {
self.write_str_all(s).map(|_| s.len())
}
fn write_str_all(&mut self, s: &str) -> fmt::Result {
self.upstream.write_str(s)
}
}
impl<'a, W> RenderAnnotated<Annotation> for ColorWrite<'a, W>
where
W: fmt::Write,
{
fn push_annotation(&mut self, annotation: &Annotation) -> Result<(), Self::Error> {
use Annotation::*;
match annotation {
Emphasized => {
self.write_str(BOLD_CODE)?;
}
Url => {
self.write_str(UNDERLINE_CODE)?;
}
PlainText => {
self.write_str(self.palette.primary)?;
}
CodeBlock => {
self.write_str(self.palette.code_block)?;
}
TypeVariable => {
self.write_str(self.palette.type_variable)?;
}
Alias => {
self.write_str(self.palette.alias)?;
}
BinOp => {
self.write_str(self.palette.alias)?;
}
Symbol => {
self.write_str(self.palette.variable)?;
}
GutterBar => {
self.write_str(self.palette.gutter_bar)?;
}
Error => {
self.write_str(self.palette.error)?;
}
LineNumber => {
self.write_str(self.palette.line_number)?;
}
Structure => {
self.write_str(self.palette.structure)?;
}
Module => {
self.write_str(self.palette.module_name)?;
}
GlobalTag | PrivateTag | RecordField | Keyword => {
self.write_str("`")?;
}
}
self.style_stack.push(*annotation);
Ok(())
}
fn pop_annotation(&mut self) -> Result<(), Self::Error> {
use Annotation::*;
match self.style_stack.pop() {
None => {}
Some(annotation) => match annotation {
Emphasized | Url | TypeVariable | Alias | Symbol | BinOp | Error | GutterBar
| Structure | CodeBlock | PlainText | LineNumber | Module => {
self.write_str(RESET_CODE)?;
}
GlobalTag | PrivateTag | RecordField | Keyword => {
self.write_str("`")?;
}
},
}
Ok(())
}
}
impl ReportText { impl ReportText {
/// Render to CI console output, where no colors are available. /// Render to CI console output, where no colors are available.
pub fn render_ci( pub fn render_ci(
@ -311,161 +445,14 @@ impl ReportText {
src_lines: &[&str], src_lines: &[&str],
interns: &Interns, interns: &Interns,
) { ) {
let env = CiEnv { let allocator = BoxAllocator;
home,
src_lines,
interns,
};
self.render_ci_help(&env, buf, subs, 0); let err_msg = "<buffer is not a utf-8 encoded string>";
}
fn render_ci_help(self, env: &CiEnv, buf: &mut String, subs: &mut Subs, indent: usize) { self.pretty::<_>(&allocator, subs, home, src_lines, interns)
use ReportText::*; .1
.render_raw(70, &mut CiWrite::new(buf))
match self { .expect(err_msg);
Plain(string) => buf.push_str(&string),
EmText(string) => {
// Since this is CI, the best we can do for emphasis are asterisks.
buf.push('*');
buf.push_str(&string);
buf.push('*');
}
GlobalTag(string) | Keyword(string) => {
// Since this is CI, the best we can do for code text is backticks.
buf.push('`');
buf.push_str(&string);
buf.push('`');
}
RecordField(string) => {
// Since this is CI, the best we can do for code text is backticks.
buf.push('`');
buf.push('.');
buf.push_str(&string);
buf.push('`');
}
Url(url) => {
buf.push('<');
buf.push_str(&url);
buf.push('>');
}
PrivateTag(symbol) | Value(symbol) => {
if symbol.module_id() == env.home {
// Render it unqualified if it's in the current module.
buf.push_str(symbol.ident_string(env.interns));
} else {
buf.push_str(symbol.module_string(env.interns));
buf.push('.');
buf.push_str(symbol.ident_string(env.interns));
}
}
Module(module_id) => {
buf.push_str(&env.interns.module_name(module_id));
}
Type(content) => {
buf.push_str(content_to_string(content, subs, env.home, env.interns).as_str())
}
ErrorType(error_type) => {
buf.push('\n');
buf.push_str(" ".repeat(indent).as_str());
buf.push_str(&write_error_type(env.home, env.interns, error_type));
buf.push('\n');
}
Region(region) => {
buf.push('\n');
buf.push('\n');
// widest displayed line number
let max_line_number_length = (region.end_line + 1).to_string().len();
if region.start_line == region.end_line {
let i = region.start_line;
let line_number_string = (i + 1).to_string();
let line_number = line_number_string.as_str();
let this_line_number_length = line_number.len();
buf.push_str(
" ".repeat(max_line_number_length - this_line_number_length)
.as_str(),
);
buf.push_str(line_number);
buf.push_str("");
let line = env.src_lines[i as usize];
if !line.trim().is_empty() {
buf.push_str(" ");
buf.push_str(env.src_lines[i as usize]);
}
buf.push('\n');
buf.push_str(" ".repeat(max_line_number_length).as_str());
buf.push_str("");
buf.push_str(" ".repeat(region.start_col as usize + 2).as_str());
buf.push_str(
"^".repeat((region.end_col - region.start_col) as usize)
.as_str(),
);
} else {
for i in region.start_line..=region.end_line {
let i_one_indexed = i + 1;
let line_number_string = i_one_indexed.to_string();
let line_number = line_number_string.as_str();
let this_line_number_length = line_number.len();
buf.push_str(
" ".repeat(max_line_number_length - this_line_number_length)
.as_str(),
);
buf.push_str(line_number);
buf.push_str(" ┆>");
let line = env.src_lines[i as usize];
if !line.trim().is_empty() {
buf.push_str(" ");
buf.push_str(env.src_lines[i as usize]);
}
if i != region.end_line {
buf.push('\n');
}
}
}
buf.push('\n');
buf.push('\n');
}
Indent(n, nested) => {
nested.render_ci_help(env, buf, subs, indent + n);
}
Docs(_) => {
panic!("TODO implment docs");
}
Concat(report_texts) => {
for report_text in report_texts {
report_text.render_ci_help(env, buf, subs, indent);
}
}
Stack(report_texts) => {
let mut it = report_texts.into_iter().peekable();
while let Some(report_text) = it.next() {
report_text.render_ci_help(env, buf, subs, indent);
buf.push('\n');
if it.peek().is_some() {
buf.push_str(" ".repeat(indent).as_str());
}
}
}
BinOp(bin_op) => {
buf.push_str(bin_op.to_string().as_str());
}
}
} }
/// Render to a color terminal using ANSI escape sequences /// Render to a color terminal using ANSI escape sequences
@ -478,142 +465,219 @@ impl ReportText {
interns: &Interns, interns: &Interns,
palette: &Palette, palette: &Palette,
) { ) {
let allocator = BoxAllocator;
let err_msg = "<buffer is not a utf-8 encoded string>";
self.pretty::<_>(&allocator, subs, home, src_lines, interns)
.1
.render_raw(70, &mut ColorWrite::new(palette, buf))
.expect(err_msg);
}
/// General idea: this function puts all the characters in. Any styling (emphasis, colors,
/// monospace font, etc) is done in the CiWrite and ColorWrite `RenderAnnotated` instances.
pub fn pretty<'b, D>(
self,
allocator: &'b D,
subs: &mut Subs,
home: ModuleId,
src_lines: &'b [&'b str],
interns: &Interns,
) -> DocBuilder<'b, D, Annotation>
where
D: DocAllocator<'b, Annotation>,
D::Doc: Clone,
{
use ReportText::*; use ReportText::*;
match self { match self {
Plain(string) => { Plain(string) => allocator
buf.push_str(&palette.primary.render(&string)); .text(format!("{}", string))
.annotate(Annotation::PlainText),
EmText(string) => allocator
.text(format!("{}", string))
.annotate(Annotation::Emphasized),
Url(url) => allocator.text(format!("{}", url)).annotate(Annotation::Url),
Keyword(string) => allocator
.text(format!("{}", string))
.annotate(Annotation::Keyword),
GlobalTag(string) => allocator
.text(format!("{}", string))
.annotate(Annotation::GlobalTag),
RecordField(string) => allocator
.text(format!(".{}", string))
.annotate(Annotation::RecordField),
PrivateTag(symbol) => {
if symbol.module_id() == home {
// Render it unqualified if it's in the current module.
allocator
.text(format!("{}", symbol.ident_string(interns)))
.annotate(Annotation::PrivateTag)
} else {
allocator
.text(format!(
"{}.{}",
symbol.module_string(interns),
symbol.ident_string(interns),
))
.annotate(Annotation::PrivateTag)
} }
EmText(string) => {
buf.push_str(&bold(&string));
}
Url(url) => {
buf.push_str(&underline(&url));
} }
Value(symbol) => { Value(symbol) => {
if symbol.module_id() == home { if symbol.module_id() == home {
// Render it unqualified if it's in the current module. // Render it unqualified if it's in the current module.
buf.push_str(&palette.variable.render(symbol.ident_string(interns))); allocator
.text(format!("{}", symbol.ident_string(interns)))
.annotate(Annotation::Symbol)
} else { } else {
let mut module_str = String::new(); allocator
.text(format!(
"{}.{}",
symbol.module_string(interns),
symbol.ident_string(interns),
))
.annotate(Annotation::Symbol)
}
}
module_str.push_str(symbol.module_string(interns)); Module(module_id) => allocator
module_str.push('.'); .text(format!("{}", interns.module_name(module_id)))
module_str.push_str(symbol.ident_string(interns)); .annotate(Annotation::Module),
buf.push_str(&palette.variable.render(&module_str));
}
}
Module(module_id) => {
buf.push_str(&palette.module_name.render(&interns.module_name(module_id)));
}
Type(content) => match content { Type(content) => match content {
Content::FlexVar(flex_var) => buf.push_str(&palette.flex_var.render( Content::FlexVar(_) | Content::RigidVar(_) => allocator
content_to_string(Content::FlexVar(flex_var), subs, home, interns).as_str(), .text(content_to_string(content, subs, home, interns))
)), .annotate(Annotation::TypeVariable),
Content::RigidVar(rigid_var) => buf.push_str(&palette.rigid_var.render(
content_to_string(Content::RigidVar(rigid_var), subs, home, interns).as_str(), Content::Structure(_) => allocator
)), .text(content_to_string(content, subs, home, interns))
Content::Structure(structure) => buf.push_str(&palette.structure.render( .annotate(Annotation::Structure),
// TODO give greater specificity to how structures are colored. Empty record colored differently than tags, etc.
content_to_string(Content::Structure(structure), subs, home, interns).as_str(), Content::Alias(_, _, _) => allocator
)), .text(content_to_string(content, subs, home, interns))
Content::Alias(symbol, vars, var) => buf.push_str( .annotate(Annotation::Alias),
&palette.alias.render(
content_to_string(Content::Alias(symbol, vars, var), subs, home, interns) Content::Error => allocator.text(content_to_string(content, subs, home, interns)),
.as_str(),
),
),
Content::Error => {}
}, },
ErrorType(error_type) => buf.push_str(&write_error_type(home, interns, error_type)), ErrorType(error_type) => allocator
Region(region) => { .nil()
// newline before snippet .append(allocator.hardline())
buf.push('\n'); .append(
buf.push('\n'); allocator
.text(write_error_type(home, interns, error_type))
.indent(4),
)
.append(allocator.hardline()),
// the widest line number that is rendered
let max_line_number_length = (region.end_line + 1).to_string().len();
if region.start_line == region.end_line {
// single line
let i = region.start_line;
let i_one_indexed = i + 1;
let line_number_string = i_one_indexed.to_string();
let line_number = line_number_string.as_str();
let this_line_number_length = line_number.len();
buf.push_str(
" ".repeat(max_line_number_length - this_line_number_length)
.as_str(),
);
buf.push_str(&palette.line_number.render(line_number));
buf.push_str(&palette.gutter_bar.render(""));
let line = src_lines[i as usize];
if !line.trim().is_empty() {
buf.push_str(" ");
buf.push_str(&palette.code_block.render(src_lines[i as usize]));
}
buf.push('\n');
buf.push_str(" ".repeat(max_line_number_length).as_str());
buf.push_str(&palette.gutter_bar.render(""));
buf.push_str(" ".repeat(region.start_col as usize + 2).as_str());
let carets = "^".repeat((region.end_col - region.start_col) as usize);
buf.push_str(&palette.error.render(carets.as_str()));
} else {
// multiline
for i in region.start_line..=region.end_line {
let i_one_indexed = i + 1;
let line_number_string = i_one_indexed.to_string();
let line_number = line_number_string.as_str();
let this_line_number_length = line_number.len();
buf.push_str(
" ".repeat(max_line_number_length - this_line_number_length)
.as_str(),
);
buf.push_str(&palette.line_number.render(line_number));
buf.push_str(&palette.gutter_bar.render(""));
buf.push_str(&palette.error.render(">"));
let line = src_lines[i as usize];
if !line.trim().is_empty() {
buf.push_str(" ");
buf.push_str(&palette.code_block.render(src_lines[i as usize]));
}
if i != region.end_line {
buf.push('\n');
}
}
}
// newline before next line of text
buf.push('\n');
buf.push('\n');
}
Indent(n, nested) => { Indent(n, nested) => {
buf.push_str(" ".repeat(n).as_str()); let rest = nested.pretty(allocator, subs, home, src_lines, interns);
nested.render_color_terminal(buf, subs, home, src_lines, interns, palette); allocator.nil().append(rest).indent(n)
} }
Concat(report_texts) => { Docs(_) => {
for report_text in report_texts { panic!("TODO implment docs");
report_text.render_color_terminal(buf, subs, home, src_lines, interns, palette);
} }
Concat(report_texts) => allocator.concat(
report_texts
.into_iter()
.map(|rep| rep.pretty(allocator, subs, home, src_lines, interns)),
),
Stack(report_texts) => allocator.intersperse(
report_texts
.into_iter()
.map(|rep| (rep.pretty(allocator, subs, home, src_lines, interns))),
allocator.hardline(),
),
BinOp(bin_op) => allocator
.text(bin_op.to_string())
.annotate(Annotation::BinOp),
Region(region) => {
let max_line_number_length = (region.end_line + 1).to_string().len();
let indent = 2;
let body = if region.start_line == region.end_line {
let i = region.start_line;
let line_number_string = (i + 1).to_string();
let line_number = line_number_string;
let this_line_number_length = line_number.len();
let line = src_lines[i as usize];
let rest_of_line = if line.trim().is_empty() {
allocator.nil()
} else {
allocator
.nil()
.append(allocator.text(line).indent(2))
.annotate(Annotation::CodeBlock)
};
let source_line = allocator
.line()
.append(
allocator
.text(" ".repeat(max_line_number_length - this_line_number_length)),
)
.append(allocator.text(line_number).annotate(Annotation::LineNumber))
.append(allocator.text("").annotate(Annotation::GutterBar))
.append(rest_of_line);
let highlight_line = allocator
.line()
.append(allocator.text(" ".repeat(max_line_number_length)))
.append(allocator.text("").annotate(Annotation::GutterBar))
.append(
allocator
.text(" ".repeat(region.start_col as usize))
.indent(indent),
)
.append(
allocator
.text("^".repeat((region.end_col - region.start_col) as usize))
.annotate(Annotation::Error),
);
source_line.append(highlight_line)
} else {
let mut result = allocator.nil();
for i in region.start_line..=region.end_line {
let line_number_string = (i + 1).to_string();
let line_number = line_number_string;
let this_line_number_length = line_number.len();
let line = src_lines[i as usize];
let rest_of_line = if !line.trim().is_empty() {
allocator
.text(line)
.annotate(Annotation::CodeBlock)
.indent(indent)
} else {
allocator.nil()
};
let source_line = allocator
.line()
.append(
allocator.text(
" ".repeat(max_line_number_length - this_line_number_length),
),
)
.append(allocator.text(line_number).annotate(Annotation::LineNumber))
.append(allocator.text("").annotate(Annotation::GutterBar))
.append(allocator.text(">").annotate(Annotation::Error))
.append(rest_of_line);
result = result.append(source_line);
}
result
};
allocator
.nil()
.append(allocator.line())
.append(body)
.append(allocator.line())
.append(allocator.line())
} }
BinOp(bin_op) => {
buf.push_str(&palette.binop.render(bin_op.to_string().as_str()));
}
_ => panic!("TODO implement more ReportTexts in render color terminal"),
} }
} }
} }

View file

@ -253,9 +253,9 @@ fn type_comparison(
ReportText::Stack(vec![ ReportText::Stack(vec![
i_am_seeing, i_am_seeing,
with_indent(4, comparison.actual), comparison.actual,
instead_of, instead_of,
with_indent(4, comparison.expected), comparison.expected,
context_hints, context_hints,
problems_to_hint(comparison.problems), problems_to_hint(comparison.problems),
]) ])
@ -271,7 +271,7 @@ fn lone_type(
ReportText::Stack(vec![ ReportText::Stack(vec![
i_am_seeing, i_am_seeing,
with_indent(4, comparison.actual), comparison.actual,
further_details, further_details,
problems_to_hint(comparison.problems), problems_to_hint(comparison.problems),
]) ])
@ -372,7 +372,7 @@ fn to_circular_report(
Region(region), Region(region),
Stack(vec![ Stack(vec![
plain_text("Here is my best effort at writing down the type. You will see ∞ for parts of the type that repeat something already printed out infinitely."), plain_text("Here is my best effort at writing down the type. You will see ∞ for parts of the type that repeat something already printed out infinitely."),
with_indent(4, type_in_focus(overall_type)), type_in_focus(overall_type),
/* TODO hint */ /* TODO hint */
]), ]),
]; ];

View file

@ -776,7 +776,6 @@ mod test_reporting {
Str Str
but I need every `if` condition to evaluate to a Booleither `True` or `False`. but I need every `if` condition to evaluate to a Booleither `True` or `False`.
"# "#
), ),
) )
@ -806,7 +805,6 @@ mod test_reporting {
Num a Num a
instead. I need all branches in an `if` to have the same type! instead. I need all branches in an `if` to have the same type!
"# "#
), ),
) )
@ -862,7 +860,6 @@ mod test_reporting {
Here is my best effort at writing down the type. You will see for parts of the type that repeat something already printed out infinitely. Here is my best effort at writing down the type. You will see for parts of the type that repeat something already printed out infinitely.
-> a -> a
"# "#
), ),
) )
@ -888,7 +885,6 @@ mod test_reporting {
Here is my best effort at writing down the type. You will see for parts of the type that repeat something already printed out infinitely. Here is my best effort at writing down the type. You will see for parts of the type that repeat something already printed out infinitely.
List -> a List -> a
"# "#
), ),
) )