mod components;
mod equations;
mod expressions;
mod visitor_impl;
use super::FormatOptions;
use crate::ir::ast::Import;
#[derive(Debug, Clone)]
pub struct CommentInfo {
pub text: String,
pub line: u32,
}
pub struct FormatVisitor {
pub options: FormatOptions,
pub indent_str: String,
pub output: String,
pub indent_level: usize,
comments: Vec<CommentInfo>,
next_comment_idx: usize,
current_line: u32,
pub source: Option<String>,
}
impl FormatVisitor {
pub fn new(options: &FormatOptions) -> Self {
let indent_str = if options.use_tabs {
"\t".to_string()
} else {
" ".repeat(options.indent_size)
};
Self {
options: options.clone(),
indent_str,
output: String::new(),
indent_level: 0,
comments: Vec::new(),
next_comment_idx: 0,
current_line: 1,
source: None,
}
}
pub fn with_comments_and_source(
options: &FormatOptions,
comments: Vec<CommentInfo>,
source: &str,
) -> Self {
let indent_str = if options.use_tabs {
"\t".to_string()
} else {
" ".repeat(options.indent_size)
};
Self {
options: options.clone(),
indent_str,
output: String::new(),
indent_level: 0,
comments,
next_comment_idx: 0,
current_line: 1,
source: Some(source.to_string()),
}
}
pub fn emit_comments_before_line(&mut self, target_line: u32) {
while self.next_comment_idx < self.comments.len() {
let comment = &self.comments[self.next_comment_idx];
if comment.line < target_line {
self.output.push_str(&self.indent());
self.output.push_str(comment.text.trim_end());
self.output.push('\n');
self.current_line += 1;
self.next_comment_idx += 1;
} else {
break;
}
}
}
fn get_trailing_comments(&mut self, source_line: u32) -> String {
let mut trailing = String::new();
while self.next_comment_idx < self.comments.len() {
let comment = &self.comments[self.next_comment_idx];
if comment.line == source_line {
trailing.push(' ');
trailing.push_str(comment.text.trim_end());
self.next_comment_idx += 1;
} else if comment.line > source_line {
break;
} else {
self.next_comment_idx += 1;
}
}
trailing
}
pub fn emit_remaining_comments(&mut self) {
while self.next_comment_idx < self.comments.len() {
let comment = &self.comments[self.next_comment_idx];
self.output.push_str(&self.indent());
self.output.push_str(comment.text.trim_end());
self.output.push('\n');
self.next_comment_idx += 1;
}
}
pub fn indent(&self) -> String {
self.indent_str.repeat(self.indent_level)
}
pub fn write(&mut self, s: &str) {
self.output.push_str(s);
}
pub fn writeln(&mut self, s: &str) {
self.output.push_str(&self.indent());
self.output.push_str(s);
self.output.push('\n');
}
pub fn writeln_with_trailing(&mut self, s: &str, source_line: u32) {
self.output.push_str(&self.indent());
self.output.push_str(s);
let trailing = self.get_trailing_comments(source_line);
self.output.push_str(&trailing);
self.output.push('\n');
}
pub fn write_with_trailing(&mut self, formatted: &str, source_line: u32) {
if let Some(without_newline) = formatted.strip_suffix('\n') {
self.output.push_str(without_newline);
let trailing = self.get_trailing_comments(source_line);
self.output.push_str(&trailing);
self.output.push('\n');
} else {
self.output.push_str(formatted);
let trailing = self.get_trailing_comments(source_line);
self.output.push_str(&trailing);
}
}
pub fn format_import(&self, import: &Import) -> String {
match import {
Import::Qualified { path, .. } => format!("import {};", path),
Import::Renamed { alias, path, .. } => {
format!("import {} = {};", alias.text, path)
}
Import::Unqualified { path, .. } => format!("import {}.*;", path),
Import::Selective { path, names, .. } => {
let name_list: Vec<&str> = names.iter().map(|t| t.text.as_str()).collect();
format!("import {}.{{{}}};", path, name_list.join(", "))
}
}
}
}