Skip to content

Commit d759ac6

Browse files
author
Manuel Mendez
committed
Convert from working with comment char to String
Git since v2.45 has supported multi character comment prefixes, so we need to convert from char to String.
1 parent 5322285 commit d759ac6

4 files changed

Lines changed: 65 additions & 62 deletions

File tree

src/commitmsgfmt.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,20 @@ use unicode_segmentation::UnicodeSegmentation;
77
pub struct CommitMsgFmt {
88
/// Max width of the message body; not used for the subject line.
99
width: usize,
10-
/// The character that identifies a comment when used in column 0 of a line.
11-
comment_char: char,
10+
/// The string that identifies a comment when started in column 0 of a line.
11+
comment_string: String,
1212
}
1313

1414
impl CommitMsgFmt {
15-
pub fn new(width: usize, comment_char: char) -> CommitMsgFmt {
15+
pub fn new(width: usize, comment_string: &str) -> CommitMsgFmt {
1616
CommitMsgFmt {
1717
width,
18-
comment_char,
18+
comment_string: comment_string.into(),
1919
}
2020
}
2121

2222
pub fn filter(&self, input: &str) -> String {
23-
let msg = parse(input, self.comment_char);
23+
let msg = parse(input, &self.comment_string);
2424
// The output size can be less than the input size only if the input contains characters
2525
// that will be trimmed, such as leading whitespace, which is improbable. It is more likely
2626
// the output size will exceed the input size due to injected linefeeds and continuation
@@ -76,7 +76,7 @@ impl CommitMsgFmt {
7676
None => self.width,
7777
};
7878
let mut cur_line_len = 0;
79-
for word in WordIter::new(paragraph, self.comment_char) {
79+
for word in WordIter::new(paragraph, &self.comment_string) {
8080
let word_len = word.graphemes(true).count();
8181

8282
// Not a new line so we need to fiddle with whitespace.
@@ -105,7 +105,7 @@ mod tests {
105105
use pretty_assertions::assert_eq;
106106

107107
fn filter(w: usize, s: &str) -> String {
108-
CommitMsgFmt::new(w, '#').filter(s)
108+
CommitMsgFmt::new(w, "#").filter(s)
109109
}
110110

111111
#[test]
@@ -657,7 +657,7 @@ foo
657657
}
658658

659659
#[test]
660-
fn preserves_scissored_content_with_custom_comment_char() {
660+
fn preserves_scissored_content_with_custom_comment_string() {
661661
let input = "
662662
foo
663663
@@ -683,7 +683,7 @@ preserve
683683
684684
content
685685
";
686-
let fmt = CommitMsgFmt::new(72, ';');
686+
let fmt = CommitMsgFmt::new(72, ";");
687687
assert_eq!(fmt.filter(input), expected);
688688
}
689689

src/main.rs

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ enum ConfigArgument<'a> {
144144
#[derive(Debug, Eq, PartialEq)]
145145
pub struct Config {
146146
width: usize,
147-
comment_char: char,
147+
comment_string: String,
148148
}
149149

150150
impl Config {
@@ -167,7 +167,7 @@ impl Config {
167167

168168
let cfg = Config {
169169
width: width as usize,
170-
comment_char: parse_git_config_commentchar(git_config_commentchar()),
170+
comment_string: parse_git_config_commentchar(git_config_commentchar()),
171171
};
172172

173173
Ok(cfg)
@@ -181,16 +181,16 @@ fn git_config_commentchar() -> Result<Vec<u8>, io::Error> {
181181
.map(|o| o.stdout)
182182
}
183183

184-
fn parse_git_config_commentchar(git_output: Result<Vec<u8>, io::Error>) -> char {
184+
fn parse_git_config_commentchar(git_output: Result<Vec<u8>, io::Error>) -> String {
185185
let output: Vec<u8> = git_output.unwrap_or_else(|_| "#".into());
186186

187187
// The setting is either unset, "auto", or precisely 1 ASCII character;
188188
// Git won't commit with an invalid configuration value. "auto" support
189189
// can be added on-demand, it requires at least 2 passes.
190190
if output.is_empty() || output == b"auto" {
191-
'#'
191+
"#".into()
192192
} else {
193-
output[0].into()
193+
(output[0] as char).into()
194194
}
195195
}
196196

@@ -202,7 +202,7 @@ fn main() -> ExitCode {
202202
}
203203
let cfg = cfg.unwrap();
204204

205-
let commitmsgfmt = commitmsgfmt::CommitMsgFmt::new(cfg.width, cfg.comment_char);
205+
let commitmsgfmt = commitmsgfmt::CommitMsgFmt::new(cfg.width, &cfg.comment_string);
206206

207207
let result = read_all_bytes_from_stdin()
208208
.and_then(to_utf8)
@@ -503,49 +503,49 @@ mod tests {
503503
vec!["binary"],
504504
Ok(Config {
505505
width: 72,
506-
comment_char: '#',
506+
comment_string: "#".into(),
507507
}),
508508
));
509509
matrix.push((
510510
vec!["binary", "--width"],
511511
Ok(Config {
512512
width: 72,
513-
comment_char: '#',
513+
comment_string: "#".into(),
514514
}),
515515
));
516516
matrix.push((
517517
vec!["binary", "--width", "10"],
518518
Ok(Config {
519519
width: 10,
520-
comment_char: '#',
520+
comment_string: "#".into(),
521521
}),
522522
));
523523
matrix.push((
524524
vec!["binary", "--width=21"],
525525
Ok(Config {
526526
width: 21,
527-
comment_char: '#',
527+
comment_string: "#".into(),
528528
}),
529529
));
530530
matrix.push((
531531
vec!["binary", "-w"],
532532
Ok(Config {
533533
width: 72,
534-
comment_char: '#',
534+
comment_string: "#".into(),
535535
}),
536536
));
537537
matrix.push((
538538
vec!["binary", "-w37"],
539539
Ok(Config {
540540
width: 37,
541-
comment_char: '#',
541+
comment_string: "#".into(),
542542
}),
543543
));
544544
matrix.push((
545545
vec!["binary", "-w37", "-w42"],
546546
Ok(Config {
547547
width: 42,
548-
comment_char: '#',
548+
comment_string: "#".into(),
549549
}),
550550
));
551551
matrix.push((
@@ -620,18 +620,18 @@ mod tests {
620620
#[test]
621621
fn parses_git_config_commentchar() {
622622
let matrix = vec![
623-
(Ok("".into()), '#'),
624-
(Ok("auto".into()), '#'),
625-
(Ok("#".into()), '#'),
626-
(Ok("xy".into()), 'x'),
627-
(Err(io::Error::from(io::ErrorKind::PermissionDenied)), '#'),
623+
(Ok("".into()), "#"),
624+
(Ok("auto".into()), "#"),
625+
(Ok("#".into()), "#"),
626+
(Ok("xy".into()), "x"),
627+
(Err(io::Error::from(io::ErrorKind::PermissionDenied)), "#"),
628628
];
629629
let (actual, expected): (Vec<_>, Vec<_>) = matrix
630630
.into_iter()
631631
.map(|(input, expected)| {
632632
let x = format!("{:?}", &input);
633633
let actual = parse_git_config_commentchar(input);
634-
((x.clone(), actual), (x, expected))
634+
((x.clone(), actual), (x, expected.into()))
635635
})
636636
.unzip();
637637
assert_eq!(expected, actual);

src/parser.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ impl CodeFence<'_> {
4646
}
4747
}
4848

49-
pub fn parse(input: &str, comment_char: char) -> Vec<Token> {
49+
pub fn parse<'a>(input: &'a str, comment_string: &str) -> Vec<Token<'a>> {
5050
let mut toks = Vec::new();
5151

5252
let mut has_subject = false;
@@ -65,7 +65,7 @@ pub fn parse(input: &str, comment_char: char) -> Vec<Token> {
6565
} else if let Some(fence) = line_as_code_fence(line) {
6666
toks.push(Token::FencedCodeBlock(line));
6767
in_code_fence = Some(fence);
68-
} else if line.starts_with(comment_char) {
68+
} else if line.starts_with(comment_string) {
6969
let t = if &line[1..] == " ------------------------ >8 ------------------------" {
7070
has_scissors = true;
7171
Token::Scissored(line)
@@ -419,7 +419,7 @@ mod tests {
419419
use pretty_assertions::assert_eq;
420420

421421
fn parse(s: &str) -> Vec<Token> {
422-
super::parse(s, '#')
422+
super::parse(s, "#")
423423
}
424424

425425
#[test]
@@ -503,13 +503,13 @@ mod tests {
503503

504504
#[test]
505505
fn parses_default_comment() {
506-
assert_eq!(super::parse("# foo", '#'), [Comment("# foo")]);
506+
assert_eq!(super::parse("# foo", "#"), [Comment("# foo")]);
507507
}
508508

509509
#[test]
510510
fn parses_custom_comment() {
511-
assert_eq!(super::parse("@ foo", '@'), [Comment("@ foo")]);
512-
assert_eq!(super::parse("# foo", '@'), [Subject("# foo")]);
511+
assert_eq!(super::parse("@ foo", "@"), [Comment("@ foo")]);
512+
assert_eq!(super::parse("# foo", "@"), [Subject("# foo")]);
513513
}
514514

515515
#[test]
@@ -1713,7 +1713,7 @@ do
17131713
}
17141714

17151715
#[test]
1716-
fn parses_scissored_content_with_custom_comment_char() {
1716+
fn parses_scissored_content_with_custom_comment_string() {
17171717
assert_eq!(
17181718
super::parse(
17191719
"
@@ -1727,7 +1727,7 @@ $ ------------------------ >8 ------------------------
17271727
do
17281728
not
17291729
format
1730-
", '$'
1730+
", "$"
17311731
),
17321732
[
17331733
VerticalSpace,

src/worditer.rs

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ use std::borrow::Cow;
55
/// definition and the exact details are unspecified. Rather, the iterator promises to produce
66
/// results that wrap safely and sensibly.
77
pub(crate) struct WordIter<'text> {
8-
/// The commit message comment character, to avoid creating a word that starts with the comment
9-
/// character lest that word degenerates into a comment.
10-
comment_char: char,
8+
/// The commit message comment string, to avoid creating a word that starts with the comment
9+
/// string lest that word degenerates into a comment.
10+
comment_string: String,
1111
/// The "next word", if present, may be considered the head of a list whose rest is
1212
/// [`Self::naive_words`] before the next invocation of [`Self::next()`]. It was the last word
1313
/// extracted from `naive_words` on the previous invocation of `next()` that was determined to
@@ -19,16 +19,16 @@ pub(crate) struct WordIter<'text> {
1919
}
2020

2121
impl<'text> WordIter<'text> {
22-
pub fn new(text: &'text str, comment_char: char) -> Self {
22+
pub fn new(text: &'text str, comment_string: &'text str) -> Self {
2323
WordIter {
24-
comment_char,
24+
comment_string: comment_string.into(),
2525
next_word: None,
2626
naive_words: text.split(' '),
2727
}
2828
}
2929

3030
fn is_non_breaking_word(&self, word: &str) -> bool {
31-
word.starts_with(self.comment_char)
31+
word.starts_with(&self.comment_string)
3232
|| match WordIter::describe_word(word) {
3333
WordJoinerState::FootnoteRefUnseen => true,
3434
WordJoinerState::FootnoteRefOpen => false,
@@ -121,7 +121,7 @@ mod tests {
121121
type Item<'text> = <WordIter<'text> as Iterator>::Item;
122122

123123
fn iter(text: &str) -> WordIter {
124-
WordIter::new(text, '#')
124+
WordIter::new(text, "#")
125125
}
126126

127127
fn collect(it: WordIter) -> Vec<Item> {
@@ -132,9 +132,12 @@ mod tests {
132132
collect(iter(text))
133133
}
134134

135-
fn some_comment_char() -> char {
136-
let some_comment_chars = ['#', ';', '!', '%'];
137-
*some_comment_chars.choose(&mut rand::thread_rng()).unwrap()
135+
fn some_comment_string() -> String {
136+
let some_comment_strings = ["#", ";", "!", "%"].map(|c| c.to_string());
137+
some_comment_strings
138+
.choose(&mut rand::thread_rng())
139+
.unwrap()
140+
.clone()
138141
}
139142

140143
#[test]
@@ -190,15 +193,15 @@ mod tests {
190193
}
191194

192195
#[test]
193-
fn merges_comment_char() {
194-
let comment_char = some_comment_char();
196+
fn merges_comment_string() {
197+
let comment_string = some_comment_string();
195198

196-
let text = format!("a {}1b d", comment_char);
199+
let text = format!("a {}1b d", comment_string);
197200
let res = {
198-
let it = WordIter::new(&text, comment_char);
201+
let it = WordIter::new(&text, &comment_string);
199202
collect(it)
200203
};
201-
let expect = [&format!("a {}1b", comment_char), "d"];
204+
let expect = [&format!("a {}1b", comment_string), "d"];
202205

203206
assert_eq!(res, expect);
204207
}
@@ -212,29 +215,29 @@ mod tests {
212215
}
213216

214217
#[test]
215-
fn lone_comment_char_binds_left() {
218+
fn lone_comment_string_binds_left() {
216219
// This is a limitation of the text analysis heuristic. The test case is
217-
// a special-case of "merges_comment_char()" made explicit.
220+
// a special-case of "merges_comment_string()" made explicit.
218221
//
219-
// The first space after a comment character marks the end of a word,
220-
// that comment character being the last part of the word.
222+
// The first space after a comment string marks the end of a word,
223+
// that comment string being the last part of the word.
221224
// This means that "a #1" and "a # 1" behave differently, producing
222-
// 1 respectively 2 words, the comment character always in the same word
225+
// 1 respectively 2 words, the comment string always in the same word
223226
// as the preceding token:
224-
// - The comment character must join the preceding token to avoid being
227+
// - The comment string must join the preceding token to avoid being
225228
// pushed onto its own line and accidentally degrading into a comment.
226-
// - The token after the comment character cannot join the preceding
229+
// - The token after the comment string cannot join the preceding
227230
// token because we have no way to determine that that token or any of
228231
// the subsequent tokens should be individual words or parts of the
229232
// first token -- this is the least surprising heuristic we can apply.
230-
let comment_char = some_comment_char();
233+
let comment_string = some_comment_string();
231234

232-
let text = format!("a {} b", comment_char);
235+
let text = format!("a {} b", comment_string);
233236
let res = {
234-
let it = WordIter::new(&text, comment_char);
237+
let it = WordIter::new(&text, &comment_string);
235238
collect(it)
236239
};
237-
let expect = [&format!("a {}", comment_char), "b"];
240+
let expect = [&format!("a {}", comment_string), "b"];
238241

239242
assert_eq!(res, expect);
240243
}

0 commit comments

Comments
 (0)