@@ -686,11 +686,48 @@ format_traceability_matrix <- function(
686
686
checkmate :: assert_logical(wrap_cols )
687
687
688
688
if (! is.null(exports_df )){
689
- # ## Exported Functions ###
690
- # Unnest tests and testing directories
689
+ if (" exported_function" %in% names(exports_df )) {
690
+ # Align internal scoring with external format.
691
+ exports_df <- dplyr :: rename(exports_df , entrypoint = " exported_function" )
692
+ }
693
+
694
+ entry_name <- switch (scorecard_type ,
695
+ " R" = " Exported Function" ,
696
+ " cli" = " Command" ,
697
+ " Entry Point"
698
+ )
699
+ exports_df <- dplyr :: rename(exports_df , !! entry_name : = " entrypoint" )
700
+
701
+ # Unnest and wrap collapsed columns to display
691
702
exported_func_df <- exports_df %> %
692
- mutate(
693
- test_files = purrr :: map_chr(.data $ test_files , ~ paste(.x , collapse = " \n " ))
703
+ dplyr :: mutate(
704
+ dplyr :: across(
705
+ all_of(c(entry_name , " code_file" , " documentation" )),
706
+ function (col ){
707
+ purrr :: map_chr(col , function (cell ){
708
+ # Replace NA values with empty strings for display purposes
709
+ cell [is.na(cell )] <- " "
710
+ # Wrap text
711
+ if (isTRUE(wrap_cols )){
712
+ cell <- wrap_text(cell , width = 24 , indent = TRUE , strict = TRUE )
713
+ }
714
+ paste(cell , collapse = " \n " )
715
+ })
716
+ }
717
+ ),
718
+ # Tests can be longer due to page width (pg_width) settings (we make it wider)
719
+ test_files = purrr :: map_chr(.data $ test_files , function (tests ){
720
+ # Replace NA values with empty strings for display purposes
721
+ tests [is.na(tests )] <- " "
722
+ # Wrap text
723
+ if (isTRUE(wrap_cols )){
724
+ # flextable will make its own new line at rendering at 35 characters
725
+ # given the current column widths. A width of 34 is therefore the largest
726
+ # we can allow if indents are desired (they wont get triggered by flextable's auto newlines)
727
+ tests <- wrap_text(tests , width = 34 , indent = TRUE , strict = TRUE )
728
+ }
729
+ paste(tests , collapse = " \n " )
730
+ })
694
731
)
695
732
696
733
# Get testing directories for caption
@@ -703,40 +740,13 @@ format_traceability_matrix <- function(
703
740
test_dirs <- NULL
704
741
}
705
742
706
- if (" exported_function" %in% names(exported_func_df )) {
707
- # Align internal scoring with external format.
708
- exported_func_df <- dplyr :: rename(exported_func_df ,
709
- entrypoint = " exported_function"
710
- )
711
- }
712
-
713
- entry_name <- switch (scorecard_type ,
714
- " R" = " Exported Function" ,
715
- " cli" = " Command" ,
716
- " Entry Point"
717
- )
718
- exported_func_df <- dplyr :: rename(
719
- exported_func_df ,
720
- !! entry_name : = " entrypoint"
721
- )
722
-
723
- # Format Table
724
- if (isTRUE(wrap_cols )){
725
- exported_func_df <- exported_func_df %> %
726
- dplyr :: mutate(
727
- dplyr :: across(
728
- all_of(c(entry_name , " code_file" , " documentation" )),
729
- function (x ) wrap_text(x , width = 24 , indent = TRUE , strict = TRUE )
730
- ),
731
- # Tests can be longer due to page width (pg_width) settings (we make it wider)
732
- test_files = purrr :: map_chr(.data $ test_files , function (tests ){
733
- wrap_text(tests , width = 40 , strict = TRUE )
734
- })
735
- )
736
- }
737
- exported_func_df <- exported_func_df %> % format_colnames_to_title()
743
+ # For exports that span more than a page (usually due to many tests), split
744
+ # the entry into `export` and `export (cont.)`. This is necessary to ensure
745
+ # tables do not overflow into the footer
746
+ exported_func_df <- split_long_rows(exported_func_df , n = 40 )
738
747
739
748
# Create flextable
749
+ exported_func_df <- exported_func_df %> % format_colnames_to_title()
740
750
exported_func_flex <- flextable_formatted(exported_func_df , pg_width = 7 , font_size = 9 ) %> %
741
751
flextable :: set_caption(" Traceability Matrix" )
742
752
@@ -757,6 +767,100 @@ format_traceability_matrix <- function(
757
767
}
758
768
}
759
769
770
+
771
+ # ' Split Long Rows in a Data Frame by Newline Count
772
+ # '
773
+ # ' This function processes a data frame to split rows where the content in
774
+ # ' specific columns exceeds a specified number of newline characters.
775
+ # ' Rows with content that spans more than `n` lines will be split into
776
+ # ' multiple rows, with the excess content carried over into continuation
777
+ # ' rows. The new rows will be labeled with "(cont.)" to indicate continuation.
778
+ # '
779
+ # ' @param exported_func_df A data frame containing columns `code_file`,
780
+ # ' `documentation`, and `test_files`, where the contents are character strings
781
+ # ' that may span multiple lines.
782
+ # ' @param n An integer specifying the maximum number of newline characters
783
+ # ' allowed in each row's content. Default is 40.
784
+ # '
785
+ # ' @return A data frame with rows split based on newline character count.
786
+ # ' Each original row that exceeds the specified number of lines will be split
787
+ # ' into multiple rows with continuation labels.
788
+ # '
789
+ # ' @note This function assumes the `entry_name`, a column determined early on in
790
+ # ' `format_traceability_matrix`, is the first column in the dataframe.
791
+ # ' @examples
792
+ # ' \dontrun{
793
+ # '
794
+ # ' split_df <- split_long_rows(exported_func_df)
795
+ # ' }
796
+ # ' @keywords internal
797
+ split_long_rows <- function (exported_func_df , n = 40 ) {
798
+
799
+ # Helper function to split content by newlines
800
+ split_by_newlines <- function (content , n ) {
801
+ content_split <- strsplit(content , " \n " )[[1 ]]
802
+ split_list <- split(content_split , ceiling(seq_along(content_split ) / n ))
803
+ lapply(split_list , paste , collapse = " \n " )
804
+ }
805
+
806
+ entry_name <- names(exported_func_df )[1 ]
807
+ temp_cols <- c(" code_file_split" , " documentation_split" , " test_files_split" , " n_lines" )
808
+
809
+ # Split contents and create new rows
810
+ exported_func_df %> %
811
+ dplyr :: mutate(
812
+ # Split contents if n_lines > n
813
+ code_file_split = purrr :: map(.data $ code_file , ~ split_by_newlines(.x , n = n )),
814
+ documentation_split = purrr :: map(.data $ documentation , ~ split_by_newlines(.x , n = n )),
815
+ test_files_split = purrr :: map(.data $ test_files , ~ split_by_newlines(.x , n = n ))
816
+ ) %> %
817
+ dplyr :: rowwise() %> %
818
+ # Expand each row if any of the splits are greater than 1
819
+ dplyr :: group_split() %> %
820
+ purrr :: map_dfr(function (row_data ) {
821
+ n_chunks <- max(length(row_data $ code_file_split [[1 ]]),
822
+ length(row_data $ documentation_split [[1 ]]),
823
+ length(row_data $ test_files_split [[1 ]]))
824
+
825
+ # Create a list of new rows
826
+ purrr :: map_dfr(1 : n_chunks , function (i ) {
827
+ new_row <- row_data
828
+
829
+ # Extract split contents or default to an empty string
830
+ new_row $ code_file <- ifelse(
831
+ length(new_row $ code_file_split [[1 ]]) > = i ,
832
+ new_row $ code_file_split [[1 ]][[i ]],
833
+ " "
834
+ )
835
+ new_row $ documentation <- ifelse(
836
+ length(new_row $ documentation_split [[1 ]]) > = i ,
837
+ new_row $ documentation_split [[1 ]][[i ]],
838
+ " "
839
+ )
840
+ new_row $ test_files <- ifelse(
841
+ length(new_row $ test_files_split [[1 ]]) > = i ,
842
+ new_row $ test_files_split [[1 ]][[i ]],
843
+ " "
844
+ )
845
+
846
+ # Label the continuation rows
847
+ new_row [[entry_name ]] <- ifelse(i == 1 , new_row [[entry_name ]], paste0(new_row [[entry_name ]], " (cont.)" ))
848
+
849
+ # Recalculate the n_lines for the new row
850
+ new_row $ n_lines <- max(
851
+ length(strsplit(new_row $ code_file , " \n " )[[1 ]]),
852
+ length(strsplit(new_row $ documentation , " \n " )[[1 ]]),
853
+ length(strsplit(new_row $ test_files , " \n " )[[1 ]])
854
+ )
855
+
856
+ new_row
857
+ })
858
+ }) %> %
859
+ dplyr :: ungroup() %> %
860
+ dplyr :: select(- all_of(temp_cols ))
861
+ }
862
+
863
+
760
864
# ' Print boiler plate text about the traceability matrix
761
865
# ' @inheritParams format_traceability_matrix
762
866
# ' @noRd
@@ -798,7 +902,7 @@ format_appendix <- function(extra_notes_data, return_vals = FALSE, scorecard_typ
798
902
cov_results_df <- cov_results_df %> %
799
903
dplyr :: mutate(
800
904
code_file = wrap_text(.data $ code_file ,
801
- width = 43 , indent = TRUE , strict = TRUE
905
+ width = 43 , indent = TRUE , strict = TRUE
802
906
),
803
907
test_coverage = sprintf(" %.2f%%" , .data $ test_coverage )
804
908
) %> %
0 commit comments