diff --git a/formatter/src/code_builder.rs b/formatter/src/code_builder.rs index 19c5e70a10d..0e8b61cc8c8 100644 --- a/formatter/src/code_builder.rs +++ b/formatter/src/code_builder.rs @@ -3,12 +3,12 @@ use std::{ str::Chars, }; -use super::parse_helpers::{clean_all_incoming_whitespace, is_comment}; use super::{ code_line::CodeLine, parse_helpers::{ - handle_ampersand_case, handle_assignment_case, handle_colon_case, handle_dash_case, - handle_pipe_case, handle_string_case, handle_whitespace_case, + clean_all_incoming_whitespace, handle_ampersand_case, handle_assignment_case, + handle_colon_case, handle_dash_case, handle_multiline_comment_case, handle_pipe_case, + handle_string_case, handle_whitespace_case, is_comment, }, }; @@ -45,7 +45,9 @@ impl CodeBuilder { pub fn format_and_add(&mut self, line: &str) { let mut code_line = self.get_unfinished_code_line_or_new(); - let line = if !code_line.is_string { + let is_string_or_multiline_comment = code_line.is_string || code_line.is_multiline_comment; + + let line = if !is_string_or_multiline_comment { line.trim() } else { line @@ -57,17 +59,23 @@ impl CodeBuilder { return self.complete_and_add_line(code_line); } - // handle multiline string - if code_line.is_string { + // add newline if it's multiline string or comment + if is_string_or_multiline_comment { code_line.push_char('\n'); } let mut iter = line.chars().enumerate().peekable(); loop { - if let Some((_, current_char)) = iter.next() { + if let Some((current_index, current_char)) = iter.next() { if code_line.is_string { handle_string_case(&mut code_line, current_char); + } else if code_line.is_multiline_comment { + handle_multiline_comment_case(&mut code_line, current_char, &mut iter); + if !code_line.is_multiline_comment { + self.complete_and_add_line(code_line); + return self.move_rest_to_new_line(line, iter); + } } else { match current_char { ' ' => handle_whitespace_case(&mut code_line, &mut iter), @@ -80,7 +88,23 @@ impl CodeBuilder { ',' => code_line.push_str(", "), '+' => code_line.append_with_whitespace("+ "), '*' => code_line.append_with_whitespace("* "), - '/' => code_line.append_with_whitespace("- "), + '/' => { + match iter.peek() { + Some((_, '*')) => { + // it's a multiline comment + code_line.become_multiline_comment(); + iter.next(); + code_line.push_str("/*"); + } + Some((_, '/')) => { + // it's a comment + let comment = &line[current_index..]; + code_line.append_with_whitespace(&comment); + return self.complete_and_add_line(code_line); + } + _ => code_line.append_with_whitespace("/ "), + } + } '%' => code_line.append_with_whitespace("% "), '^' => code_line.append_with_whitespace("^ "), '!' => code_line.append_with_whitespace("!"), @@ -155,7 +179,7 @@ impl CodeBuilder { // case when '}' was separated from ';' by one or more new lines if previous_code_line.is_completed { // remove empty line first - if !(previous_code_line.text.chars().last().unwrap_or(' ') == '}') { + if !(previous_code_line.text.chars().last() == Some('}')) { self.edits.pop(); } @@ -196,6 +220,7 @@ impl CodeBuilder { } // if there is more - move to new line! Some(_) => { + self.complete_and_add_line(CodeLine::new("}".into())); self.move_rest_to_new_line(line, iter); } None => { @@ -207,11 +232,19 @@ impl CodeBuilder { fn move_rest_to_new_line(&mut self, line: &str, iter: Peekable>) { let mut iter = iter; - if iter.peek().is_some() { - let (next_index, _) = iter.peek().unwrap(); + if let Some((next_index, _)) = iter.peek() { + let next_line = &line[*next_index..].trim(); - let next_line = &line[*next_index..]; - self.format_and_add(next_line); + // if rest is comment append it to the last existing line + if is_comment(&next_line) { + if let Some(mut code_line) = self.edits.pop() { + code_line.push_char(' '); + code_line.push_str(next_line); + self.add_line(code_line); + } + } else { + self.format_and_add(next_line); + } } } diff --git a/formatter/src/code_line.rs b/formatter/src/code_line.rs index 91d0178a27a..e0ea76eabe6 100644 --- a/formatter/src/code_line.rs +++ b/formatter/src/code_line.rs @@ -3,6 +3,7 @@ pub struct CodeLine { pub text: String, pub is_string: bool, pub is_completed: bool, + pub is_multiline_comment: bool, pub was_previously_stored: bool, } @@ -12,6 +13,7 @@ impl CodeLine { text, is_string: false, is_completed: false, + is_multiline_comment: false, was_previously_stored: false, } } @@ -21,6 +23,7 @@ impl CodeLine { text: "".into(), is_string: false, is_completed: false, + is_multiline_comment: false, was_previously_stored: false, } } @@ -30,6 +33,7 @@ impl CodeLine { text: "".into(), is_string: false, is_completed: true, + is_multiline_comment: false, was_previously_stored: false, } } @@ -50,6 +54,14 @@ impl CodeLine { self.is_string = true; } + pub fn become_multiline_comment(&mut self) { + self.is_multiline_comment = true; + } + + pub fn end_multiline_comment(&mut self) { + self.is_multiline_comment = false; + } + pub fn end_string(&mut self) { self.is_string = false; } @@ -61,11 +73,7 @@ impl CodeLine { pub fn append_with_whitespace(&mut self, value: &str) { let last = self.text.chars().last(); - let is_previous_whitespace = if last.is_none() { - true - } else { - last.unwrap() == ' ' - }; + let is_previous_whitespace = Some(' ') == last; if !is_previous_whitespace { self.push_char(' '); @@ -74,6 +82,25 @@ impl CodeLine { self.push_str(value); } + pub fn append_equal_sign(&mut self) { + let last = self.text.chars().last(); + + if Some('!') == last { + self.push_char('='); + } else { + self.append_with_whitespace("="); + } + } + + pub fn append_whitespace(&mut self) { + let last = self.text.chars().last(); + + match last { + Some('(') => {} // do not add whitespace, + _ => self.append_with_whitespace(""), + } + } + pub fn is_empty(&self) -> bool { self.text.is_empty() } diff --git a/formatter/src/parse_helpers.rs b/formatter/src/parse_helpers.rs index f8c5c24bf87..3db065ab4bc 100644 --- a/formatter/src/parse_helpers.rs +++ b/formatter/src/parse_helpers.rs @@ -10,13 +10,29 @@ pub fn is_comment(line: &str) -> bool { chars.next() == Some('/') && chars.next() == Some('/') } +pub fn handle_multiline_comment_case( + code_line: &mut CodeLine, + current_char: char, + iter: &mut Peekable>, +) { + code_line.push_char(current_char); + + if current_char == '*' { + // end multiline + if let Some((_, '/')) = iter.peek() { + code_line.push_char('/'); + iter.next(); + code_line.end_multiline_comment(); + } + } +} // if it's a string just keep pushing the characters pub fn handle_string_case(code_line: &mut CodeLine, current_char: char) { code_line.push_char(current_char); if current_char == '"' { - let previous_char = code_line.text.chars().last().unwrap_or(' '); + let previous_char = code_line.text.chars().last(); // end of the string - if previous_char != '\\' { + if previous_char != Some('\\') { code_line.end_string(); } } @@ -29,10 +45,10 @@ pub fn handle_whitespace_case(code_line: &mut CodeLine, iter: &mut Peekable {} // do nothing, handle it in next turn + '(' | ';' | ':' | ')' => {} // do nothing, handle it in next turn _ => { // add whitespace if it is not already there - code_line.append_with_whitespace(""); + code_line.append_whitespace(); } } } @@ -50,8 +66,7 @@ pub fn handle_assignment_case(code_line: &mut CodeLine, iter: &mut Peekable "); iter.next(); } else { - // it's assignment - code_line.append_with_whitespace("= "); + code_line.append_equal_sign(); } } else { code_line.append_with_whitespace("= ");