Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-core/src/comment.rs')
| -rw-r--r-- | helix-core/src/comment.rs | 183 |
1 files changed, 56 insertions, 127 deletions
diff --git a/helix-core/src/comment.rs b/helix-core/src/comment.rs index 5985cac7..536b710a 100644 --- a/helix-core/src/comment.rs +++ b/helix-core/src/comment.rs @@ -4,30 +4,11 @@ use smallvec::SmallVec; use crate::{ - syntax::config::BlockCommentToken, Change, Range, Rope, RopeSlice, Selection, Tendril, - Transaction, + syntax::BlockCommentToken, Change, Range, Rope, RopeSlice, Selection, Tendril, Transaction, }; use helix_stdx::rope::RopeSliceExt; use std::borrow::Cow; -pub const DEFAULT_COMMENT_TOKEN: &str = "#"; - -/// Returns the longest matching comment token of the given line (if it exists). -pub fn get_comment_token<'a, S: AsRef<str>>( - text: RopeSlice, - tokens: &'a [S], - line_num: usize, -) -> Option<&'a str> { - let line = text.line(line_num); - let start = line.first_non_whitespace_char()?; - - tokens - .iter() - .map(AsRef::as_ref) - .filter(|token| line.slice(start..).starts_with(token)) - .max_by_key(|token| token.len()) -} - /// Given text, a comment token, and a set of line indices, returns the following: /// - Whether the given lines should be considered commented /// - If any of the lines are uncommented, all lines are considered as such. @@ -47,20 +28,21 @@ fn find_line_comment( let mut min = usize::MAX; // minimum col for first_non_whitespace_char let mut margin = 1; let token_len = token.chars().count(); - for line in lines { let line_slice = text.line(line); if let Some(pos) = line_slice.first_non_whitespace_char() { let len = line_slice.len_chars(); - min = std::cmp::min(min, pos); + if pos < min { + min = pos; + } // line can be shorter than pos + token len let fragment = Cow::from(line_slice.slice(pos..std::cmp::min(pos + token.len(), len))); - // as soon as one of the non-blank lines doesn't have a comment, the whole block is - // considered uncommented. if fragment != token { + // as soon as one of the non-blank lines doesn't have a comment, the whole block is + // considered uncommented. commented = false; } @@ -74,7 +56,6 @@ fn find_line_comment( to_change.push(line); } } - (commented, to_change, min, margin) } @@ -82,7 +63,7 @@ fn find_line_comment( pub fn toggle_line_comments(doc: &Rope, selection: &Selection, token: Option<&str>) -> Transaction { let text = doc.slice(..); - let token = token.unwrap_or(DEFAULT_COMMENT_TOKEN); + let token = token.unwrap_or("//"); let comment = Tendril::from(format!("{} ", token)); let mut lines: Vec<usize> = Vec::with_capacity(selection.len()); @@ -148,7 +129,10 @@ pub fn find_block_comments( let mut only_whitespace = true; let mut comment_changes = Vec::with_capacity(selection.len()); let default_tokens = tokens.first().cloned().unwrap_or_default(); + // TODO: check if this can be removed on MSRV bump + #[allow(clippy::redundant_clone)] let mut start_token = default_tokens.start.clone(); + #[allow(clippy::redundant_clone)] let mut end_token = default_tokens.end.clone(); let mut tokens = tokens.to_vec(); @@ -205,9 +189,13 @@ pub fn find_block_comments( range: *range, start_pos, end_pos, - start_margin: selection_slice.get_char(after_start) == Some(' '), + start_margin: selection_slice + .get_char(after_start) + .map_or(false, |c| c == ' '), end_margin: after_start != before_end - && (selection_slice.get_char(before_end) == Some(' ')), + && selection_slice + .get_char(before_end) + .map_or(false, |c| c == ' '), start_token: start_token.to_string(), end_token: end_token.to_string(), }); @@ -329,87 +317,56 @@ pub fn split_lines_of_selection(text: RopeSlice, selection: &Selection) -> Selec mod test { use super::*; - mod find_line_comment { - use super::*; - - #[test] - fn not_commented() { - // four lines, two space indented, except for line 1 which is blank. - let doc = Rope::from(" 1\n\n 2\n 3"); - - let text = doc.slice(..); - - let res = find_line_comment("//", text, 0..3); - // (commented = false, to_change = [line 0, line 2], min = col 2, margin = 0) - assert_eq!(res, (false, vec![0, 2], 2, 0)); - } - - #[test] - fn is_commented() { - // three lines where the second line is empty. - let doc = Rope::from("// hello\n\n// there"); - - let res = find_line_comment("//", doc.slice(..), 0..3); - - // (commented = true, to_change = [line 0, line 2], min = col 0, margin = 1) - assert_eq!(res, (true, vec![0, 2], 0, 1)); - } - } - - // TODO: account for uncommenting with uneven comment indentation - mod toggle_line_comment { - use super::*; - - #[test] - fn comment() { - // four lines, two space indented, except for line 1 which is blank. - let mut doc = Rope::from(" 1\n\n 2\n 3"); - // select whole document - let selection = Selection::single(0, doc.len_chars() - 1); + #[test] + fn test_find_line_comment() { + // four lines, two space indented, except for line 1 which is blank. + let mut doc = Rope::from(" 1\n\n 2\n 3"); + // select whole document + let mut selection = Selection::single(0, doc.len_chars() - 1); - let transaction = toggle_line_comments(&doc, &selection, None); - transaction.apply(&mut doc); + let text = doc.slice(..); - assert_eq!(doc, " # 1\n\n # 2\n # 3"); - } + let res = find_line_comment("//", text, 0..3); + // (commented = true, to_change = [line 0, line 2], min = col 2, margin = 0) + assert_eq!(res, (false, vec![0, 2], 2, 0)); - #[test] - fn uncomment() { - let mut doc = Rope::from(" # 1\n\n # 2\n # 3"); - let mut selection = Selection::single(0, doc.len_chars() - 1); + // comment + let transaction = toggle_line_comments(&doc, &selection, None); + transaction.apply(&mut doc); + selection = selection.map(transaction.changes()); - let transaction = toggle_line_comments(&doc, &selection, None); - transaction.apply(&mut doc); - selection = selection.map(transaction.changes()); + assert_eq!(doc, " // 1\n\n // 2\n // 3"); - assert_eq!(doc, " 1\n\n 2\n 3"); - assert!(selection.len() == 1); // to ignore the selection unused warning - } + // uncomment + let transaction = toggle_line_comments(&doc, &selection, None); + transaction.apply(&mut doc); + selection = selection.map(transaction.changes()); + assert_eq!(doc, " 1\n\n 2\n 3"); + assert!(selection.len() == 1); // to ignore the selection unused warning - #[test] - fn uncomment_0_margin_comments() { - let mut doc = Rope::from(" #1\n\n #2\n #3"); - let mut selection = Selection::single(0, doc.len_chars() - 1); + // 0 margin comments + doc = Rope::from(" //1\n\n //2\n //3"); + // reset the selection. + selection = Selection::single(0, doc.len_chars() - 1); - let transaction = toggle_line_comments(&doc, &selection, None); - transaction.apply(&mut doc); - selection = selection.map(transaction.changes()); + let transaction = toggle_line_comments(&doc, &selection, None); + transaction.apply(&mut doc); + selection = selection.map(transaction.changes()); + assert_eq!(doc, " 1\n\n 2\n 3"); + assert!(selection.len() == 1); // to ignore the selection unused warning - assert_eq!(doc, " 1\n\n 2\n 3"); - assert!(selection.len() == 1); // to ignore the selection unused warning - } + // 0 margin comments, with no space + doc = Rope::from("//"); + // reset the selection. + selection = Selection::single(0, doc.len_chars() - 1); - #[test] - fn uncomment_0_margin_comments_with_no_space() { - let mut doc = Rope::from("#"); - let mut selection = Selection::single(0, doc.len_chars() - 1); + let transaction = toggle_line_comments(&doc, &selection, None); + transaction.apply(&mut doc); + selection = selection.map(transaction.changes()); + assert_eq!(doc, ""); + assert!(selection.len() == 1); // to ignore the selection unused warning - let transaction = toggle_line_comments(&doc, &selection, None); - transaction.apply(&mut doc); - selection = selection.map(transaction.changes()); - assert_eq!(doc, ""); - assert!(selection.len() == 1); // to ignore the selection unused warning - } + // TODO: account for uncommenting with uneven comment indentation } #[test] @@ -456,32 +413,4 @@ mod test { transaction.apply(&mut doc); assert_eq!(doc, ""); } - - /// Test, if `get_comment_tokens` works, even if the content of the file includes chars, whose - /// byte size unequal the amount of chars - #[test] - fn test_get_comment_with_char_boundaries() { - let rope = Rope::from("ยทยท"); - let tokens = ["//", "///"]; - - assert_eq!( - super::get_comment_token(rope.slice(..), tokens.as_slice(), 0), - None - ); - } - - /// Test for `get_comment_token`. - /// - /// Assuming the comment tokens are stored as `["///", "//"]`, `get_comment_token` should still - /// return `///` instead of `//` if the user is in a doc-comment section. - #[test] - fn test_use_longest_comment() { - let text = Rope::from(" /// amogus"); - let tokens = ["///", "//"]; - - assert_eq!( - super::get_comment_token(text.slice(..), tokens.as_slice(), 0), - Some("///") - ); - } } |