Skip to content

Commit 77a6755

Browse files
authored
Merge pull request #5966 from Ideflop/more-implement-argument-pattern
More implement argument pattern
2 parents f14aa81 + a227af7 commit 77a6755

File tree

2 files changed

+138
-9
lines changed

2 files changed

+138
-9
lines changed

src/uu/more/src/more.rs

+88-9
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ struct Options {
5454
clean_print: bool,
5555
from_line: usize,
5656
lines: Option<u16>,
57+
pattern: Option<String>,
5758
print_over: bool,
5859
silent: bool,
5960
squeeze: bool,
@@ -75,10 +76,14 @@ impl Options {
7576
Some(number) if number > 1 => number - 1,
7677
_ => 0,
7778
};
79+
let pattern = matches
80+
.get_one::<String>(options::PATTERN)
81+
.map(|s| s.to_owned());
7882
Self {
7983
clean_print: matches.get_flag(options::CLEAN_PRINT),
8084
from_line,
8185
lines,
86+
pattern,
8287
print_over: matches.get_flag(options::PRINT_OVER),
8388
silent: matches.get_flag(options::SILENT),
8489
squeeze: matches.get_flag(options::SQUEEZE),
@@ -206,6 +211,15 @@ pub fn uu_app() -> Command {
206211
.action(ArgAction::SetTrue)
207212
.hide(true),
208213
)
214+
.arg(
215+
Arg::new(options::PATTERN)
216+
.short('P')
217+
.long(options::PATTERN)
218+
.allow_hyphen_values(true)
219+
.required(false)
220+
.value_name("pattern")
221+
.help("Display file beginning from pattern match"),
222+
)
209223
.arg(
210224
Arg::new(options::FROM_LINE)
211225
.short('F')
@@ -245,14 +259,6 @@ pub fn uu_app() -> Command {
245259
.long(options::NO_PAUSE)
246260
.help("Suppress pause after form feed"),
247261
)
248-
.arg(
249-
Arg::new(options::PATTERN)
250-
.short('P')
251-
.allow_hyphen_values(true)
252-
.required(false)
253-
.takes_value(true)
254-
.help("Display file beginning from pattern match"),
255-
)
256262
*/
257263
.arg(
258264
Arg::new(options::FILES)
@@ -307,6 +313,17 @@ fn more(
307313

308314
let mut pager = Pager::new(rows, lines, next_file, options);
309315

316+
if options.pattern.is_some() {
317+
match search_pattern_in_file(&pager.lines, &options.pattern) {
318+
Some(number) => pager.upper_mark = number,
319+
None => {
320+
execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine))?;
321+
stdout.write_all("\rPattern not found\n".as_bytes())?;
322+
pager.content_rows -= 1;
323+
}
324+
}
325+
}
326+
310327
if multiple_file {
311328
execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
312329
stdout.write_all(
@@ -592,6 +609,19 @@ impl<'a> Pager<'a> {
592609
}
593610
}
594611

612+
fn search_pattern_in_file(lines: &[String], pattern: &Option<String>) -> Option<usize> {
613+
let pattern = pattern.clone().unwrap_or_default();
614+
if lines.is_empty() || pattern.is_empty() {
615+
return None;
616+
}
617+
for (line_number, line) in lines.iter().enumerate() {
618+
if line.contains(pattern.as_str()) {
619+
return Some(line_number);
620+
}
621+
}
622+
None
623+
}
624+
595625
fn paging_add_back_message(options: &Options, stdout: &mut std::io::Stdout) -> UResult<()> {
596626
if options.lines.is_some() {
597627
execute!(stdout, MoveUp(1))?;
@@ -640,7 +670,7 @@ fn break_line(line: &str, cols: usize) -> Vec<String> {
640670

641671
#[cfg(test)]
642672
mod tests {
643-
use super::break_line;
673+
use super::{break_line, search_pattern_in_file};
644674
use unicode_width::UnicodeWidthStr;
645675

646676
#[test]
@@ -688,4 +718,53 @@ mod tests {
688718
// Each 👩🏻‍🔬 is 6 character width it break line to the closest number to 80 => 6 * 13 = 78
689719
assert_eq!((78, 42), (widths[0], widths[1]));
690720
}
721+
722+
#[test]
723+
fn test_search_pattern_empty_lines() {
724+
let lines = vec![];
725+
let pattern = Some(String::from("pattern"));
726+
assert_eq!(None, search_pattern_in_file(&lines, &pattern));
727+
}
728+
729+
#[test]
730+
fn test_search_pattern_empty_pattern() {
731+
let lines = vec![String::from("line1"), String::from("line2")];
732+
let pattern = None;
733+
assert_eq!(None, search_pattern_in_file(&lines, &pattern));
734+
}
735+
736+
#[test]
737+
fn test_search_pattern_found_pattern() {
738+
let lines = vec![
739+
String::from("line1"),
740+
String::from("line2"),
741+
String::from("pattern"),
742+
];
743+
let lines2 = vec![
744+
String::from("line1"),
745+
String::from("line2"),
746+
String::from("pattern"),
747+
String::from("pattern2"),
748+
];
749+
let lines3 = vec![
750+
String::from("line1"),
751+
String::from("line2"),
752+
String::from("other_pattern"),
753+
];
754+
let pattern = Some(String::from("pattern"));
755+
assert_eq!(2, search_pattern_in_file(&lines, &pattern).unwrap());
756+
assert_eq!(2, search_pattern_in_file(&lines2, &pattern).unwrap());
757+
assert_eq!(2, search_pattern_in_file(&lines3, &pattern).unwrap());
758+
}
759+
760+
#[test]
761+
fn test_search_pattern_not_found_pattern() {
762+
let lines = vec![
763+
String::from("line1"),
764+
String::from("line2"),
765+
String::from("something"),
766+
];
767+
let pattern = Some(String::from("pattern"));
768+
assert_eq!(None, search_pattern_in_file(&lines, &pattern));
769+
}
691770
}

tests/by-util/test_more.rs

+50
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ fn test_valid_arg() {
3434

3535
new_ucmd!().arg("-F").arg("10").succeeds();
3636
new_ucmd!().arg("--from-line").arg("0").succeeds();
37+
38+
new_ucmd!().arg("-P").arg("something").succeeds();
39+
new_ucmd!().arg("--pattern").arg("-1").succeeds();
3740
}
3841
}
3942

@@ -151,3 +154,50 @@ fn test_more_error_on_multiple_files() {
151154
.stderr_contains("file3");
152155
}
153156
}
157+
158+
#[test]
159+
fn test_more_pattern_found() {
160+
if std::io::stdout().is_terminal() {
161+
let scene = TestScenario::new(util_name!());
162+
let at = &scene.fixtures;
163+
164+
let file = "test_file";
165+
166+
at.write(file, "line1\nline2");
167+
168+
// output only the second line "line2"
169+
scene
170+
.ucmd()
171+
.arg("-P")
172+
.arg("line2")
173+
.arg(file)
174+
.succeeds()
175+
.no_stderr()
176+
.stdout_does_not_contain("line1")
177+
.stdout_contains("line2");
178+
}
179+
}
180+
181+
#[test]
182+
fn test_more_pattern_not_found() {
183+
if std::io::stdout().is_terminal() {
184+
let scene = TestScenario::new(util_name!());
185+
let at = &scene.fixtures;
186+
187+
let file = "test_file";
188+
189+
let file_content = "line1\nline2";
190+
at.write(file, file_content);
191+
192+
scene
193+
.ucmd()
194+
.arg("-P")
195+
.arg("something")
196+
.arg(file)
197+
.succeeds()
198+
.no_stderr()
199+
.stdout_contains("Pattern not found")
200+
.stdout_contains("line1")
201+
.stdout_contains("line2");
202+
}
203+
}

0 commit comments

Comments
 (0)