@@ -344,16 +344,48 @@ impl Comments {
344344 & self . comments
345345 }
346346
347- /// Helper method for checking and finding for a comment before a specific
348- /// line
347+ /// Finds a single comment before a specific line or returns none
349348 ///
350349 /// # Parameters
351- /// - `self` an instance of [`Comments`]
352- /// - `line` an `u64` value representing the desired line to check above.
350+ /// - [`Comments`] object
351+ /// - An `u64` value representing the desired line to check above.
353352 #[ must_use]
354353 pub fn leading_comment ( & self , line : u64 ) -> Option < & Comment > {
355354 self . comments ( ) . iter ( ) . rev ( ) . find ( |comment| comment. span ( ) . end ( ) . line ( ) + 1 == line)
356355 }
356+
357+ /// Finds leading comments before specific line based on [`LeadingCommentCapture`] preference
358+ ///
359+ /// # Parameters
360+ /// - [`Comments`] object
361+ /// - An `u64` value representing the desired line to check above.
362+ /// - [`LeadingCommentCapture`] preference
363+ #[ must_use]
364+ pub fn leading_comments ( & self , line : u64 , capture : & LeadingCommentCapture ) -> Vec < & Comment > {
365+ let mut comments = Vec :: new ( ) ;
366+ let mut current_line = line;
367+ let mut seen_multiline = false ;
368+ while let Some ( leading_comment) = self . leading_comment ( current_line) {
369+ match capture {
370+ LeadingCommentCapture :: SingleNearest => {
371+ comments. push ( leading_comment) ;
372+ break ;
373+ }
374+ LeadingCommentCapture :: AllLeading => comments. push ( leading_comment) ,
375+ LeadingCommentCapture :: AllSingleOneMulti => match leading_comment. kind ( ) {
376+ CommentKind :: MultiLine if seen_multiline => break ,
377+ CommentKind :: MultiLine => {
378+ seen_multiline = true ;
379+ comments. push ( leading_comment) ;
380+ }
381+ CommentKind :: SingleLine => comments. push ( leading_comment) ,
382+ } ,
383+ }
384+ current_line = leading_comment. span ( ) . start ( ) . line ( ) ;
385+ }
386+ comments. reverse ( ) ;
387+ comments
388+ }
357389}
358390
359391/// Controls how leading comments are captured for a statement.
@@ -781,4 +813,100 @@ CREATE TABLE posts (
781813 assert_eq ! ( comment. span( ) . end( ) , comment_vec[ i] . span( ) . end( ) ) ;
782814 }
783815 }
816+
817+ use crate :: comments:: LeadingCommentCapture ;
818+
819+ fn texts ( v : Vec < & Comment > ) -> Vec < String > {
820+ v. into_iter ( ) . map ( |c| c. text ( ) . to_owned ( ) ) . collect ( )
821+ }
822+
823+ #[ test]
824+ fn leading_comment_capture_default_is_single_nearest ( ) {
825+ match LeadingCommentCapture :: default ( ) {
826+ LeadingCommentCapture :: SingleNearest => { }
827+ _ => panic ! ( "Default for LeadingCommentCapture must be SingleNearest" ) ,
828+ }
829+ }
830+
831+ #[ test]
832+ fn leading_comments_single_nearest_and_all_leading_basic_runover ( )
833+ -> Result < ( ) , Box < dyn std:: error:: Error > > {
834+ let src = "\
835+ -- c1
836+ -- c2
837+ CREATE TABLE t (id INTEGER);
838+ " ;
839+ let parsed = Comments :: scan_comments ( src) ?;
840+ let single = parsed. leading_comments ( 3 , & LeadingCommentCapture :: SingleNearest ) ;
841+ assert_eq ! ( texts( single) , vec![ "c2" . to_owned( ) ] ) ;
842+
843+ let all = parsed. leading_comments ( 3 , & LeadingCommentCapture :: AllLeading ) ;
844+ assert_eq ! ( texts( all) , vec![ "c1" . to_owned( ) , "c2" . to_owned( ) ] ) ;
845+
846+ Ok ( ( ) )
847+ }
848+
849+ #[ test]
850+ fn leading_comments_all_leading_stops_at_blank_line ( ) -> Result < ( ) , Box < dyn std:: error:: Error > >
851+ {
852+ let src = "\
853+ -- c1
854+
855+ -- c2
856+ CREATE TABLE t (id INTEGER);
857+ " ;
858+ let parsed = Comments :: scan_comments ( src) ?;
859+ let all = parsed. leading_comments ( 4 , & LeadingCommentCapture :: AllLeading ) ;
860+ assert_eq ! ( texts( all) , vec![ "c2" . to_owned( ) ] ) ;
861+
862+ Ok ( ( ) )
863+ }
864+
865+ #[ test]
866+ fn leading_comments_all_single_one_multi_collects_singles_and_one_multiline ( )
867+ -> Result < ( ) , Box < dyn std:: error:: Error > > {
868+ let src = "\
869+ /* m
870+ m */
871+ -- s1
872+ -- s2
873+ CREATE TABLE t (id INTEGER);
874+ " ;
875+ let parsed = Comments :: scan_comments ( src) ?;
876+ let got = parsed. leading_comments ( 5 , & LeadingCommentCapture :: AllSingleOneMulti ) ;
877+ assert_eq ! ( texts( got) , vec![ "m\n m" . to_owned( ) , "s1" . to_owned( ) , "s2" . to_owned( ) , ] ) ;
878+
879+ Ok ( ( ) )
880+ }
881+
882+ #[ test]
883+ fn leading_comments_all_single_one_multi_stops_before_second_multiline ( )
884+ -> Result < ( ) , Box < dyn std:: error:: Error > > {
885+ let src = "\
886+ /* m1 */
887+ /* m2 */
888+ -- s1
889+ CREATE TABLE t (id INTEGER);
890+ " ;
891+ let parsed = Comments :: scan_comments ( src) ?;
892+ let got = parsed. leading_comments ( 4 , & LeadingCommentCapture :: AllSingleOneMulti ) ;
893+ assert_eq ! ( texts( got) , vec![ "m2" . to_owned( ) , "s1" . to_owned( ) ] ) ;
894+
895+ Ok ( ( ) )
896+ }
897+
898+ #[ test]
899+ fn leading_comments_single_nearest_can_return_multiline ( )
900+ -> Result < ( ) , Box < dyn std:: error:: Error > > {
901+ let src = "\
902+ /* hello
903+ world */
904+ CREATE TABLE t (id INTEGER);
905+ " ;
906+ let parsed = Comments :: scan_comments ( src) ?;
907+ let got = parsed. leading_comments ( 3 , & LeadingCommentCapture :: SingleNearest ) ;
908+ assert_eq ! ( texts( got) , vec![ "hello\n world" . to_owned( ) ] ) ;
909+
910+ Ok ( ( ) )
911+ }
784912}
0 commit comments