Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 50 additions & 9 deletions sway-fmt-v2/src/config/whitespace.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Standard system and editor whitespace configuration options. Advanced whitespace options will be deferred to their corresponding sub-classes.
use serde::{Deserialize, Serialize};

use crate::constants::{DEFAULT_MAX_LINE_WIDTH, DEFAULT_TAB_SPACES};
use crate::constants::{CARRIAGE_RETURN, DEFAULT_MAX_LINE_WIDTH, DEFAULT_TAB_SPACES, LINE_FEED};

use super::user_opts::WhitespaceOptions;

Expand Down Expand Up @@ -45,6 +45,16 @@ impl Whitespace {
}
}

/// Handling of line indentation for expressions or items.
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
pub enum IndentStyle {
/// First line on the same line as the opening brace, all lines aligned with
/// the first line.
Visual,
/// First line is on a new line and all lines align with **block** indent.
Block,
}

/// Handling of which OS new-line style should be applied.
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
pub enum NewlineStyle {
Expand All @@ -58,12 +68,43 @@ pub enum NewlineStyle {
Native,
}

/// Handling of line indentation for expressions or items.
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
pub enum IndentStyle {
/// First line on the same line as the opening brace, all lines aligned with
/// the first line.
Visual,
/// First line is on a new line and all lines align with **block** indent.
Block,
// The definitive system type for `[NewlineStyle]`.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum NewlineSystemType {
Windows,
Unix,
}

impl NewlineSystemType {
pub fn get_newline_style(newline_style: NewlineStyle, raw_input_text: &str) -> Self {
match newline_style {
NewlineStyle::Auto => Self::auto_detect_newline_style(raw_input_text),
NewlineStyle::Native => Self::native_newline_style(),
NewlineStyle::Windows => Self::Windows,
NewlineStyle::Unix => Self::Unix,
}
}

pub fn auto_detect_newline_style(raw_input_text: &str) -> Self {
let first_line_feed_pos = raw_input_text.chars().position(|ch| ch == LINE_FEED);
match first_line_feed_pos {
Some(first_line_feed_pos) => {
let char_before_line_feed_pos = first_line_feed_pos.saturating_sub(1);
let char_before_line_feed = raw_input_text.chars().nth(char_before_line_feed_pos);
match char_before_line_feed {
Some(CARRIAGE_RETURN) => Self::Windows,
_ => Self::Unix,
}
}
None => Self::native_newline_style(),
}
}

fn native_newline_style() -> Self {
if cfg!(windows) {
Self::Windows
} else {
Self::Unix
}
}
}
6 changes: 6 additions & 0 deletions sway-fmt-v2/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,9 @@ pub const DEFAULT_ENUM_VARIANT_ALIGN_THRESHOLD: usize = 0;

/// Default max length of comments.
pub const DEFAULT_MAX_COMMENT_WIDTH: usize = 80;

/////NEWLINE_STYLE/////
pub const LINE_FEED: char = '\n';
pub const CARRIAGE_RETURN: char = '\r';
pub const WINDOWS_NEWLINE: &str = "\r\n";
pub const UNIX_NEWLINE: &str = "\n";
2 changes: 2 additions & 0 deletions sway-fmt-v2/src/items/item_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ use sway_parse::ItemUse;
impl Format for ItemUse {
fn format(&self, _formatter: &Formatter) -> FormattedCode {
todo!()
// reorder use statements alphabeticaly
// break into new lines for long use statements
}
}
1 change: 1 addition & 0 deletions sway-fmt-v2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod constants;
mod error;
mod fmt;
mod items;
mod newline_style;

pub use crate::fmt::Formatter;
pub use error::FormatterError;
208 changes: 208 additions & 0 deletions sway-fmt-v2/src/newline_style.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
//! Functions and tests that determine and apply the user defined [NewlineStyle].
use crate::{
config::whitespace::{NewlineStyle, NewlineSystemType},
constants::{CARRIAGE_RETURN, LINE_FEED, UNIX_NEWLINE, WINDOWS_NEWLINE},
};

/// Apply this newline style to the formatted text. When the style is set
/// to `Auto`, the `raw_input_text` is used to detect the existing line
/// endings.
///
/// If the style is set to `Auto` and `raw_input_text` contains no
/// newlines, the `Native` style will be used.
pub(crate) fn apply_newline_style(
newline_style: NewlineStyle,
formatted_text: &mut String,
raw_input_text: &str,
) {
*formatted_text = match NewlineSystemType::get_newline_style(newline_style, raw_input_text) {
NewlineSystemType::Windows => convert_to_windows_newlines(formatted_text),
NewlineSystemType::Unix => convert_to_unix_newlines(formatted_text),
}
}

fn convert_to_windows_newlines(formatted_text: &String) -> String {
let mut transformed = String::with_capacity(2 * formatted_text.capacity());
let mut chars = formatted_text.chars().peekable();
while let Some(current_char) = chars.next() {
let next_char = chars.peek();
match current_char {
LINE_FEED => transformed.push_str(WINDOWS_NEWLINE),
CARRIAGE_RETURN if next_char == Some(&LINE_FEED) => {}
current_char => transformed.push(current_char),
}
}
transformed
}

fn convert_to_unix_newlines(formatted_text: &str) -> String {
formatted_text.replace(WINDOWS_NEWLINE, UNIX_NEWLINE)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn auto_detects_unix_newlines() {
assert_eq!(
NewlineSystemType::Unix,
NewlineSystemType::auto_detect_newline_style("One\nTwo\nThree")
);
}

#[test]
fn auto_detects_windows_newlines() {
assert_eq!(
NewlineSystemType::Windows,
NewlineSystemType::auto_detect_newline_style("One\r\nTwo\r\nThree")
);
}

#[test]
fn auto_detects_windows_newlines_with_multibyte_char_on_first_line() {
assert_eq!(
NewlineSystemType::Windows,
NewlineSystemType::auto_detect_newline_style("A 🎢 of a first line\r\nTwo\r\nThree")
);
}

#[test]
fn falls_back_to_native_newlines_if_no_newlines_are_found() {
let expected_newline_style = if cfg!(windows) {
NewlineSystemType::Windows
} else {
NewlineSystemType::Unix
};
assert_eq!(
expected_newline_style,
NewlineSystemType::auto_detect_newline_style("One Two Three")
);
}

#[test]
fn auto_detects_and_applies_unix_newlines() {
let formatted_text = "One\nTwo\nThree";
let raw_input_text = "One\nTwo\nThree";

let mut out = String::from(formatted_text);
apply_newline_style(NewlineStyle::Auto, &mut out, raw_input_text);
assert_eq!("One\nTwo\nThree", &out, "auto should detect 'lf'");
}

#[test]
fn auto_detects_and_applies_windows_newlines() {
let formatted_text = "One\nTwo\nThree";
let raw_input_text = "One\r\nTwo\r\nThree";

let mut out = String::from(formatted_text);
apply_newline_style(NewlineStyle::Auto, &mut out, raw_input_text);
assert_eq!("One\r\nTwo\r\nThree", &out, "auto should detect 'crlf'");
}

#[test]
fn auto_detects_and_applies_native_newlines() {
let formatted_text = "One\nTwo\nThree";
let raw_input_text = "One Two Three";

let mut out = String::from(formatted_text);
apply_newline_style(NewlineStyle::Auto, &mut out, raw_input_text);

if cfg!(windows) {
assert_eq!(
"One\r\nTwo\r\nThree", &out,
"auto-native-windows should detect 'crlf'"
);
} else {
assert_eq!(
"One\nTwo\nThree", &out,
"auto-native-unix should detect 'lf'"
);
}
}

#[test]
fn applies_unix_newlines() {
test_newlines_are_applied_correctly(
"One\r\nTwo\nThree",
"One\nTwo\nThree",
NewlineStyle::Unix,
);
}

#[test]
fn applying_unix_newlines_changes_nothing_for_unix_newlines() {
let formatted_text = "One\nTwo\nThree";
test_newlines_are_applied_correctly(formatted_text, formatted_text, NewlineStyle::Unix);
}

#[test]
fn applies_unix_newlines_to_string_with_unix_and_windows_newlines() {
test_newlines_are_applied_correctly(
"One\r\nTwo\r\nThree\nFour",
"One\nTwo\nThree\nFour",
NewlineStyle::Unix,
);
}

#[test]
fn applies_windows_newlines_to_string_with_unix_and_windows_newlines() {
test_newlines_are_applied_correctly(
"One\nTwo\nThree\r\nFour",
"One\r\nTwo\r\nThree\r\nFour",
NewlineStyle::Windows,
);
}

#[test]
fn applying_windows_newlines_changes_nothing_for_windows_newlines() {
let formatted_text = "One\r\nTwo\r\nThree";
test_newlines_are_applied_correctly(formatted_text, formatted_text, NewlineStyle::Windows);
}

#[test]
fn keeps_carriage_returns_when_applying_windows_newlines_to_str_with_unix_newlines() {
test_newlines_are_applied_correctly(
"One\nTwo\nThree\rDrei",
"One\r\nTwo\r\nThree\rDrei",
NewlineStyle::Windows,
);
}

#[test]
fn keeps_carriage_returns_when_applying_unix_newlines_to_str_with_unix_newlines() {
test_newlines_are_applied_correctly(
"One\nTwo\nThree\rDrei",
"One\nTwo\nThree\rDrei",
NewlineStyle::Unix,
);
}

#[test]
fn keeps_carriage_returns_when_applying_windows_newlines_to_str_with_windows_newlines() {
test_newlines_are_applied_correctly(
"One\r\nTwo\r\nThree\rDrei",
"One\r\nTwo\r\nThree\rDrei",
NewlineStyle::Windows,
);
}

#[test]
fn keeps_carriage_returns_when_applying_unix_newlines_to_str_with_windows_newlines() {
test_newlines_are_applied_correctly(
"One\r\nTwo\r\nThree\rDrei",
"One\nTwo\nThree\rDrei",
NewlineStyle::Unix,
);
}

fn test_newlines_are_applied_correctly(
input: &str,
expected: &str,
newline_style: NewlineStyle,
) {
let mut out = String::from(input);
apply_newline_style(newline_style, &mut out, input);
assert_eq!(expected, &out);
}
}