Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add list option #20

Merged
merged 2 commits into from
Nov 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
84 changes: 55 additions & 29 deletions src/chiritori.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{
code::{
formatter::{self, BlockFormatter, Formatter},
list::build_list,
remover::{
self,
marker::{
Expand All @@ -15,6 +16,7 @@ use crate::{
factory::RemoveStrategies,
},
removal_evaluator::RemovalEvaluator,
Remover,
},
},
parser, tokenizer,
Expand All @@ -25,8 +27,6 @@ use std::{
};

pub struct ChiritoriConfiguration {
pub delimiter_start: String,
pub delimiter_end: String,
pub time_limited_configuration: TimeLimitedConfiguration,
pub marker_tag_configuration: MarkerTagConfiguration,
}
Expand All @@ -42,14 +42,46 @@ pub struct MarkerTagConfiguration {
pub marker_removal_tags: HashSet<String>,
}

pub fn clean(content: Rc<String>, config: ChiritoriConfiguration) -> String {
let tokens = tokenizer::tokenize(&content, &config.delimiter_start, &config.delimiter_end);
pub fn clean(
content: Rc<String>,
delimiters: (String, String),
config: ChiritoriConfiguration,
) -> String {
let (delimiter_start, delimiter_end) = delimiters;
let tokens = tokenizer::tokenize(&content, &delimiter_start, &delimiter_end);

let parsed = parser::parse(&tokens);
let mut builder_map: HashMap<&str, Box<dyn RemovalEvaluator>> = HashMap::new();
let remover = build_remover(config, content.clone());
let (removed, markers) = remover.remove(parsed, &content);

let removed_pos = remover::get_removed_pos(&markers);
let formatter = build_formatters();
let structure_formatters: Vec<Box<dyn BlockFormatter>> = vec![Box::new(
formatter::block_indent_remover::BlockIndentRemover {},
)];

formatter::format(&removed, &removed_pos, &formatter, &structure_formatters)
}

pub fn list(
content: Rc<String>,
delimiters: (String, String),
config: ChiritoriConfiguration,
) -> String {
let (delimiter_start, delimiter_end) = delimiters;
let tokens = tokenizer::tokenize(&content, &delimiter_start, &delimiter_end);

let parsed = parser::parse(&tokens);
let remover = build_remover(config, content.clone());
let (_, markers) = remover.remove(parsed, &content);

build_list(&content, &markers)
}

fn build_remover(config: ChiritoriConfiguration, content: Rc<String>) -> Remover {
let mut builder_map: HashMap<String, Box<dyn RemovalEvaluator>> = HashMap::new();
builder_map.insert(
&config.time_limited_configuration.tag_name,
config.time_limited_configuration.tag_name,
Box::new(
remover::removal_evaluator::time_limited_evaluator::TimeLimitedEvaluator {
current_time: config.time_limited_configuration.current,
Expand All @@ -59,7 +91,7 @@ pub fn clean(content: Rc<String>, config: ChiritoriConfiguration) -> String {
);

builder_map.insert(
&config.marker_tag_configuration.tag_name,
config.marker_tag_configuration.tag_name,
Box::new(
remover::removal_evaluator::marker_evaluator::MarkerEvaluator {
marker_removal_names: config.marker_tag_configuration.marker_removal_tags,
Expand All @@ -70,30 +102,24 @@ pub fn clean(content: Rc<String>, config: ChiritoriConfiguration) -> String {
let remove_strategy_map: RemoveStrategies = vec![
(
Box::new(UnwrapBlockMarkerAvailability::new("unwrap-block")),
Box::new(UnwrapBlockMarkerBuilder {
content: Rc::clone(&content),
}),
Box::new(UnwrapBlockMarkerBuilder { content }),
),
(
Box::new(RangeMarkerAvailability::default()),
Box::new(RangeMarkerBuilder::default()),
),
];

let (removed, markers) = remover::remove(parsed, &content, &builder_map, &remove_strategy_map);
Remover::new(builder_map, remove_strategy_map)
}

let removed_pos = remover::get_removed_pos(&markers);
let formatter: Vec<Box<dyn Formatter>> = vec![
fn build_formatters() -> Vec<Box<dyn Formatter>> {
vec![
Box::new(formatter::indent_remover::IndentRemover {}),
Box::new(formatter::empty_line_remover::EmptyLineRemover {}),
Box::new(formatter::prev_line_break_remover::PrevLineBreakRemover {}),
Box::new(formatter::next_line_break_remover::NextLineBreakRemover {}),
];
let structure_formatters: Vec<Box<dyn BlockFormatter>> = vec![Box::new(
formatter::block_indent_remover::BlockIndentRemover {},
)];

formatter::format(&removed, &removed_pos, &formatter, &structure_formatters)
]
}

#[cfg(test)]
Expand All @@ -105,10 +131,8 @@ mod tests {
use std::io::prelude::*;
use std::{fs::File, path::PathBuf};

fn create_test_config(delimiter_start: &str, delimiter_end: &str) -> ChiritoriConfiguration {
fn create_test_config() -> ChiritoriConfiguration {
ChiritoriConfiguration {
delimiter_start: String::from(delimiter_start),
delimiter_end: String::from(delimiter_end),
time_limited_configuration: TimeLimitedConfiguration {
tag_name: String::from("time-limited"),
current: Local::now(),
Expand Down Expand Up @@ -163,9 +187,9 @@ mod tests {
</body>
</html>"#,
);

let config = create_test_config("<!--", "-->");
let result = clean(content.into(), config);
let config = create_test_config();
let delimiters = (String::from("<!--"), String::from("-->"));
let result = clean(content.into(), delimiters, config);

assert_eq!(result, expected);
}
Expand Down Expand Up @@ -215,8 +239,9 @@ console.log("Temporary code while feature2 is not released")
"#,
);

let config = create_test_config("/* <", "> */");
let result = clean(content.into(), config);
let config = create_test_config();
let delimiters = (String::from("/* <"), String::from("> */"));
let result = clean(content.into(), delimiters, config);

assert_eq!(result, expected);
}
Expand All @@ -242,8 +267,9 @@ console.log("Temporary code while feature2 is not released")
.read_to_string(&mut expected_content)
.expect("Failed to load an expected content file");

let config = create_test_config("/* <", "> */");
let result = clean(input_content.into(), config);
let config = create_test_config();
let delimiters = (String::from("/* <"), String::from("> */"));
let result = clean(input_content.into(), delimiters, config);

assert_eq!(result, expected_content);
}
Expand Down
1 change: 1 addition & 0 deletions src/code.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod formatter;
pub mod list;
pub mod remover;
pub mod utils;
129 changes: 129 additions & 0 deletions src/code/list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use super::{
remover::RemoveMarker,
utils::line_break_pos_finder::{find_next_line_break_pos, find_prev_line_break_pos},
};

const MARKER_START: &str = "\x1b[32m_start\x1b[0m";
const MARKER_END: &str = "\x1b[32m‾end\x1b[0m";
const START_COLOR: &str = "\x1b[31m";
const END_COLOR: &str = "\x1b[0m";
const MARKER_START_LEN: usize = MARKER_START.len();
const MARKER_END_LEN: usize = MARKER_END.len();
const START_COLOR_LEN: usize = START_COLOR.len();
const END_COLOR_LEN: usize = END_COLOR.len();

const HEAD_START: &str = "\n--------[ ";
const HEAD_END: &str = " ]--------\n";

pub fn build_item(content: &str, start: usize, end: usize) -> String {
if end - start == 0 || content.is_empty() {
return String::new();
}

let bytes = content.as_bytes();
let line_start = find_prev_line_break_pos(content, bytes, start, false)
.map(|v| v + 1)
.unwrap_or(0);
let line_end_start_pos = find_prev_line_break_pos(content, bytes, end - 1, false)
.map(|v| v + 1)
.unwrap_or(0);
let line_end =
find_next_line_break_pos(content, bytes, end - 1, false).unwrap_or(content.len());

let color_start = start;
// If the end position is a line break (= line_end < end), it is not included.
let color_end = end.min(line_end);

let marker_start_ofs_len = start - line_start;
let marker_end_ofs_len = end - line_end_start_pos - 1;
let mut result = String::with_capacity(
marker_start_ofs_len
+ MARKER_START_LEN
+ 1
+ START_COLOR_LEN
+ (line_end - line_start)
+ END_COLOR_LEN
+ 1
+ marker_end_ofs_len
+ MARKER_END_LEN,
);

result.push_str(&" ".repeat(marker_start_ofs_len));
result.push_str(MARKER_START);
result.push('\n');
result.push_str(&content[line_start..color_start]);
result.push_str(START_COLOR);
result.push_str(&content[color_start..color_end]);
result.push_str(END_COLOR);
result.push_str(&content[color_end..line_end]);
result.push('\n');
result.push_str(&" ".repeat(marker_end_ofs_len));
result.push_str(MARKER_END);

result
}

pub fn build_list(content: &str, markers: &[RemoveMarker]) -> String {
markers
.iter()
.zip(1..=markers.len())
.map(|((range, _), idx)| {
let mut res = String::from(HEAD_START);
res.push_str(&idx.to_string());
res.push_str(HEAD_END);
res.push_str(&build_item(content, range.start, range.end));

res
})
.collect()
}

#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
use std::ops::Range;

#[rstest]
// 0 10
// 012345678901234567
#[case("aaa+bbbb+ccc+dddd", 4..11, format!("{}{}+{}{}{}{}{}+{}{}", "", MARKER_START, "", START_COLOR, "bbbb+cc", END_COLOR, "c", " ", MARKER_END))]
#[case("aaa+bbbb+ccc+dddd", 3..11, format!("{}{}+{}{}{}{}{}+{}{}", " ", MARKER_START, "aaa", START_COLOR, "+bbbb+cc", END_COLOR, "c", " ", MARKER_END))]
#[case("aaa+bbbb+ccc+dddd", 2..11, format!("{}{}+{}{}{}{}{}+{}{}", " ", MARKER_START, "aa", START_COLOR, "a+bbbb+cc", END_COLOR, "c", " ", MARKER_END))]
#[case("aaa+bbbb+ccc+dddd", 4..12, format!("{}{}+{}{}{}{}{}+{}{}", "", MARKER_START, "", START_COLOR, "bbbb+ccc", END_COLOR, "", " ", MARKER_END))]
#[case("aaa+bbbb+ccc+dddd", 4..13, format!("{}{}+{}{}{}{}{}+{}{}", "", MARKER_START, "", START_COLOR, "bbbb+ccc", END_COLOR, "", " ", MARKER_END))]
#[case("aaa+bbbb+ccc+dddd", 4..16, format!("{}{}+{}{}{}{}{}+{}{}", "", MARKER_START, "", START_COLOR, "bbbb+ccc+ddd", END_COLOR, "d", " ", MARKER_END))]
#[case("abcd", 1..2, format!("{}{}+{}{}{}{}{}+{}{}", " ", MARKER_START, "a", START_COLOR, "b", END_COLOR, "cd", " ", MARKER_END))]
#[case("", 0..0, "")]
fn test_build_item(
#[case] content: &str,
#[case] range: Range<usize>,
#[case] expected: String,
) {
let content = content.replace('+', "\n");

assert_eq!(
build_item(&content, range.start, range.end),
expected.replace('+', "\n")
);
}

#[test]
fn test_build_list() {
// 0123456789012345678
let content = "aaaa+bbbb+cccc+dddd".replace('+', "\n");
let markers = [(1..2, None), (7..12, None)];

let expected_item1 = build_item(&content, 1, 2);
let expected_item2 = build_item(&content, 7, 12);

assert_eq!(
build_list(&content, &markers),
format!(
"{}1{}{}{}2{}{}",
HEAD_START, HEAD_END, expected_item1, HEAD_START, HEAD_END, expected_item2
)
.replace('+', "\n")
)
}
}
Loading
Loading