///|
pub enum TabsOrSpaces {
Tabs
Spaces
} derive(Eq, Debug)
///|
pub fn TabsOrSpaces::tabs() -> TabsOrSpaces {
Tabs
}
///|
pub fn TabsOrSpaces::spaces() -> TabsOrSpaces {
Spaces
}
///|
pub struct PrettyPrintContext {
max_line_width : UInt
tabs_or_spaces : TabsOrSpaces
tab_width : UInt
continuation_indent : UInt
source_indent_width : UInt
} derive(Eq, Debug)
///|
pub fn PrettyPrintContext::new(
max_line_width? : UInt = 100U,
tabs_or_spaces? : TabsOrSpaces = Spaces,
tab_width? : UInt = 2U,
continuation_indent? : UInt = 1U,
source_indent_width? : UInt = 2U,
) -> PrettyPrintContext {
PrettyPrintContext::{
max_line_width,
tabs_or_spaces,
tab_width,
continuation_indent,
source_indent_width,
}
}
///|
pub fn PrettyPrintContext::indent_unit(self : PrettyPrintContext) -> String {
match self.tabs_or_spaces {
Tabs => "\t"
Spaces => {
let width = if self.tab_width == 0U { 1U } else { self.tab_width }
" ".repeat(width.reinterpret_as_int())
}
}
}
///|
pub fn PrettyPrintContext::indent(
self : PrettyPrintContext,
level : UInt,
) -> String {
let out = StringBuilder::new()
let unit = self.indent_unit()
let mut i = 0
while i < level.reinterpret_as_int() {
out.write_string(unit)
i += 1
}
out.to_string()
}
///|
fn wrap_line(line : String, ctx : PrettyPrintContext) -> String {
let max_width = ctx.max_line_width.reinterpret_as_int()
if max_width == 0 || line.length() <= max_width {
return line
}
let mut leading = 0
while leading < line.length() {
let c = line[leading]
if c == ' ' || c == '\t' {
leading += 1
} else {
break
}
}
let prefix = line.sub(end=leading).to_owned()
let body = line.sub(start=leading).to_owned()
if body.length() == 0 {
return line
}
let words = body.split(" ")
let out = StringBuilder::new()
let continuation_prefix = prefix + ctx.indent(ctx.continuation_indent)
let mut current_prefix = prefix
out.write_string(current_prefix)
let mut current_width = current_prefix.length()
let mut has_word = false
for word_view in words {
let word = word_view.to_owned()
if word.length() == 0 {
continue
}
let word_width = word.length()
let needs_space = has_word
let mut next_width = current_width + word_width
if needs_space {
next_width += 1
}
if needs_space && next_width > max_width {
out.write_char('\n')
current_prefix = continuation_prefix
out.write_string(current_prefix)
current_width = current_prefix.length()
has_word = false
}
if has_word {
out.write_char(' ')
current_width += 1
}
out.write_string(word)
current_width += word_width
has_word = true
}
out.to_string()
}
///|
fn normalize_indent(line : String, ctx : PrettyPrintContext) -> String {
let mut leading = 0
let mut spaces = 0
let mut tabs = 0
while leading < line.length() {
let c = line[leading]
if c == ' ' {
spaces += 1
leading += 1
} else if c == '\t' {
tabs += 1
leading += 1
} else {
break
}
}
if leading == 0 {
return line
}
let source_unit = if ctx.source_indent_width == 0U {
1
} else {
ctx.source_indent_width.reinterpret_as_int()
}
let levels = tabs + spaces / source_unit
let content = line.sub(start=leading).to_owned()
ctx.indent(levels.reinterpret_as_uint()) + content
}
///|
pub fn apply_pretty_context(text : String, ctx : PrettyPrintContext) -> String {
let out = StringBuilder::new()
let mut first = true
for line in text.split("\n") {
if !first {
out.write_char('\n')
}
out.write_string(wrap_line(normalize_indent(line.to_owned(), ctx), ctx))
first = false
}
out.to_string()
}
///|
pub(open) trait PrettyPrint {
fn pretty_print(Self, PrettyPrintContext) -> String
}
///|
#callsite(autofill(args_loc, loc))
pub fn inspect_debug(
obj : &Debug,
content? : String = "",
loc~ : SourceLoc,
args_loc~ : ArgsLoc,
) -> Unit raise InspectError {
debug_inspect(obj, content~, loc~, args_loc~)
}
///|
#callsite(autofill(args_loc, loc))
pub fn[T : PrettyPrint] inspect_pretty_print(
obj : T,
content? : String = "",
ctx? : PrettyPrintContext = PrettyPrintContext::new(),
loc~ : SourceLoc,
args_loc~ : ArgsLoc,
) -> Unit raise InspectError {
inspect(obj.pretty_print(ctx), content~, loc~, args_loc~)
}