sley_diff_format/
funcname.rs1use sley_core::{GitError, Result};
2use sley_grep::{Regex, RegexMode};
3
4pub struct CompiledFuncname {
8 patterns: Vec<(bool, Regex)>,
9}
10
11const FUNCNAME_BUFFER: usize = 80;
14
15impl CompiledFuncname {
16 pub fn compile(spec: &[u8], extended: bool, icase: bool) -> Result<Self> {
20 let lines: Vec<&[u8]> = spec.split(|&b| b == b'\n').collect();
21 let mut patterns = Vec::new();
22 for (idx, line) in lines.iter().enumerate() {
23 let negate = line.first() == Some(&b'!');
24 if negate && idx == lines.len() - 1 {
25 let suffix: Vec<u8> = lines[idx..].join(&b'\n');
28 eprintln!(
29 "fatal: Last expression must not be negated: {}",
30 String::from_utf8_lossy(&suffix)
31 );
32 return Err(GitError::Exit(128));
33 }
34 let expression = if negate { &line[1..] } else { line };
35 let mode = if extended {
36 RegexMode::Ere
37 } else {
38 RegexMode::Bre
39 };
40 let regex = Regex::compile_bytes(expression, mode, icase, false).map_err(|_| {
41 eprintln!(
42 "fatal: Invalid regexp to look for hunk header: {}",
43 String::from_utf8_lossy(expression)
44 );
45 GitError::Exit(128)
46 })?;
47 patterns.push((negate, regex));
48 }
49 Ok(Self { patterns })
50 }
51
52 pub fn match_line(&self, line: &[u8]) -> Option<Vec<u8>> {
56 let mut len = line.len();
58 if len > 0 && line[len - 1] == b'\n' {
59 if len > 1 && line[len - 2] == b'\r' {
60 len -= 2;
61 } else {
62 len -= 1;
63 }
64 }
65 let line = &line[..len];
66 let mut matched: Option<Vec<Option<(usize, usize)>>> = None;
67 for (negate, regex) in &self.patterns {
68 if let Some(captures) = regex.find_captures(line) {
69 if *negate {
70 return None;
71 }
72 matched = Some(captures);
73 break;
74 }
75 }
76 let captures = matched?;
77 let (start, end) = captures
78 .get(1)
79 .copied()
80 .flatten()
81 .unwrap_or_else(|| captures[0].expect("whole-match span present"));
82 let heading = &line[start..end];
83 let mut result = heading.len().min(FUNCNAME_BUFFER);
84 while result > 0 && heading[result - 1].is_ascii_whitespace() {
85 result -= 1;
86 }
87 Some(heading[..result].to_vec())
88 }
89}
90
91pub fn default_funcname_heading(line: &[u8]) -> Option<Vec<u8>> {
95 let first = line.first()?;
96 if !(first.is_ascii_alphabetic() || *first == b'_' || *first == b'$') {
97 return None;
98 }
99 let mut len = line.len().min(FUNCNAME_BUFFER);
100 while len > 0 && line[len - 1].is_ascii_whitespace() {
101 len -= 1;
102 }
103 Some(line[..len].to_vec())
104}