From c15b58400a7637978119ecc837a1ca101bba3b08 Mon Sep 17 00:00:00 2001 From: Marc Bernard <59966492+mbtools@users.noreply.github.com> Date: Mon, 25 Oct 2021 10:06:27 -0400 Subject: [PATCH] Enhance diff class (#5044) * Enhance diff class - Refactor `zcl_abapgit_diff` to use `RS_CMP_COMPUTE_DELTA` instead of `SVRS_COMPUTE_DELTA_REPS` (needs less code in abapGit) - Replace `abaptxt_255` with `rswsourcet` which removes 255 character per line limit. Now diffs on long lines are calculated properly. - Add "ignore indent", "ignore comments", and "ignore case" options. These correspond to the options for the Split Screen Editor in SAP GUI and work the same way. The options are *not* exposed to the UI yet. Same functionality, same performance. * Lint Co-authored-by: Lars Hvam --- src/utils/zcl_abapgit_diff.clas.abap | 251 ++++++++---------- .../zcl_abapgit_diff.clas.testclasses.abap | 135 +++++++++- 2 files changed, 237 insertions(+), 149 deletions(-) diff --git a/src/utils/zcl_abapgit_diff.clas.abap b/src/utils/zcl_abapgit_diff.clas.abap index 9cd599e41..530aac6bb 100644 --- a/src/utils/zcl_abapgit_diff.clas.abap +++ b/src/utils/zcl_abapgit_diff.clas.abap @@ -1,37 +1,39 @@ CLASS zcl_abapgit_diff DEFINITION PUBLIC - CREATE PUBLIC . + CREATE PUBLIC. PUBLIC SECTION. CONSTANTS co_starting_beacon TYPE i VALUE 1. * assumes data is UTF8 based with newlines -* only works with lines up to 255 characters METHODS constructor IMPORTING - !iv_new TYPE xstring - !iv_old TYPE xstring . + !iv_new TYPE xstring + !iv_old TYPE xstring + !iv_ignore_indentation TYPE abap_bool DEFAULT abap_false + !iv_ignore_comments TYPE abap_bool DEFAULT abap_false + !iv_ignore_case TYPE abap_bool DEFAULT abap_false. METHODS get RETURNING - VALUE(rt_diff) TYPE zif_abapgit_definitions=>ty_diffs_tt . + VALUE(rt_diff) TYPE zif_abapgit_definitions=>ty_diffs_tt. METHODS stats RETURNING - VALUE(rs_count) TYPE zif_abapgit_definitions=>ty_count . + VALUE(rs_count) TYPE zif_abapgit_definitions=>ty_count. METHODS set_patch_new IMPORTING !iv_line_new TYPE i !iv_patch_flag TYPE abap_bool RAISING - zcx_abapgit_exception . + zcx_abapgit_exception. METHODS set_patch_old IMPORTING !iv_line_old TYPE i !iv_patch_flag TYPE abap_bool RAISING - zcx_abapgit_exception . + zcx_abapgit_exception. METHODS get_beacons RETURNING - VALUE(rt_beacons) TYPE zif_abapgit_definitions=>ty_string_tt . + VALUE(rt_beacons) TYPE zif_abapgit_definitions=>ty_string_tt. METHODS is_line_patched IMPORTING iv_index TYPE i @@ -47,38 +49,35 @@ CLASS zcl_abapgit_diff DEFINITION PROTECTED SECTION. PRIVATE SECTION. - TYPES ty_regexset_tt TYPE STANDARD TABLE OF REF TO cl_abap_regex WITH KEY table_line. - DATA mt_beacons TYPE zif_abapgit_definitions=>ty_string_tt . - DATA mt_diff TYPE zif_abapgit_definitions=>ty_diffs_tt . - DATA ms_stats TYPE zif_abapgit_definitions=>ty_count . + TYPES: + ty_regexset_tt TYPE STANDARD TABLE OF REF TO cl_abap_regex WITH KEY table_line. - CLASS-METHODS unpack + DATA mt_beacons TYPE zif_abapgit_definitions=>ty_string_tt. + DATA mt_diff TYPE zif_abapgit_definitions=>ty_diffs_tt. + DATA ms_stats TYPE zif_abapgit_definitions=>ty_count. + DATA mv_compare_mode TYPE c LENGTH 1. + DATA mv_ignore_case TYPE abap_bool. + + METHODS unpack IMPORTING !iv_new TYPE xstring !iv_old TYPE xstring EXPORTING - !et_new TYPE abaptxt255_tab - !et_old TYPE abaptxt255_tab . - CLASS-METHODS render - IMPORTING - !it_new TYPE abaptxt255_tab - !it_old TYPE abaptxt255_tab - !it_delta TYPE vxabapt255_tab - RETURNING - VALUE(rt_diff) TYPE zif_abapgit_definitions=>ty_diffs_tt . - CLASS-METHODS compute - IMPORTING - !it_new TYPE abaptxt255_tab - !it_old TYPE abaptxt255_tab - RETURNING - VALUE(rt_delta) TYPE vxabapt255_tab . - METHODS calculate_line_num_and_stats . - METHODS map_beacons . - METHODS shortlist . + !et_new TYPE rswsourcet + !et_old TYPE rswsourcet. + METHODS map_beacons. + METHODS shortlist. METHODS create_regex_set RETURNING VALUE(rt_regex_set) TYPE ty_regexset_tt. + METHODS compute_and_render + IMPORTING + !it_new TYPE rswsourcet + !it_old TYPE rswsourcet + RETURNING + VALUE(rt_diff) TYPE zif_abapgit_definitions=>ty_diffs_tt. + METHODS calculate_stats. ENDCLASS. @@ -86,31 +85,12 @@ ENDCLASS. CLASS zcl_abapgit_diff IMPLEMENTATION. - METHOD calculate_line_num_and_stats. - - DATA: lv_new TYPE i VALUE 1, - lv_old TYPE i VALUE 1. + METHOD calculate_stats. FIELD-SYMBOLS: LIKE LINE OF mt_diff. - LOOP AT mt_diff ASSIGNING . - -new_num = lv_new. - -old_num = lv_old. - - CASE -result. " Line nums - WHEN zif_abapgit_definitions=>c_diff-delete. - lv_old = lv_old + 1. - CLEAR -new_num. - WHEN zif_abapgit_definitions=>c_diff-insert. - lv_new = lv_new + 1. - CLEAR -old_num. - WHEN OTHERS. - lv_new = lv_new + 1. - lv_old = lv_old + 1. - ENDCASE. - - CASE -result. " Stats + CASE -result. WHEN zif_abapgit_definitions=>c_diff-insert. ms_stats-insert = ms_stats-insert + 1. WHEN zif_abapgit_definitions=>c_diff-delete. @@ -118,51 +98,103 @@ CLASS zcl_abapgit_diff IMPLEMENTATION. WHEN zif_abapgit_definitions=>c_diff-update. ms_stats-update = ms_stats-update + 1. ENDCASE. - ENDLOOP. ENDMETHOD. - METHOD compute. + METHOD compute_and_render. - DATA: lt_trdirtab_old TYPE TABLE OF trdir, - lt_trdirtab_new TYPE TABLE OF trdir, - lt_trdir_delta TYPE TABLE OF xtrdir. + DATA: + lv_i TYPE i, + ls_diff LIKE LINE OF rt_diff, + lt_delta TYPE STANDARD TABLE OF rsedcresul WITH DEFAULT KEY. + FIELD-SYMBOLS: + LIKE LINE OF it_old, + LIKE LINE OF it_new, + LIKE LINE OF lt_delta. - CALL FUNCTION 'SVRS_COMPUTE_DELTA_REPS' + CALL FUNCTION 'RS_CMP_COMPUTE_DELTA' + EXPORTING + compare_mode = mv_compare_mode + ignore_case_differences = mv_ignore_case TABLES - texttab_old = it_old - texttab_new = it_new - trdirtab_old = lt_trdirtab_old - trdirtab_new = lt_trdirtab_new - trdir_delta = lt_trdir_delta - text_delta = rt_delta. + text_tab1 = it_new + text_tab2 = it_old + text_tab_res = lt_delta + EXCEPTIONS + parameter_invalid = 1 + difference_not_found = 2 + OTHERS = 3. + + IF sy-subrc = 0. + " Process delta + LOOP AT lt_delta ASSIGNING . + CLEAR ls_diff. + IF -line1 > 0. + ls_diff-old_num = lv_i = -line1. + ls_diff-old = -text1. + ENDIF. + IF -line2 > 0. + ls_diff-new_num = lv_i = -line2. + ls_diff-new = -text2. + ENDIF. + IF -flag1 = 'D'. + ls_diff-result = zif_abapgit_definitions=>c_diff-delete. + ELSEIF -flag2 = 'I'. + ls_diff-result = zif_abapgit_definitions=>c_diff-insert. + ELSEIF -flag1 = 'M' AND -flag2 = 'M'. + ls_diff-result = zif_abapgit_definitions=>c_diff-update. + ELSEIF -flag1 = '' AND -flag2 = ''. + ls_diff-result = ''. + ELSE. + ASSERT 0 = 1. " unknown comparison result + ENDIF. + APPEND ls_diff TO rt_diff. + ENDLOOP. + ELSEIF sy-subrc = 2. + " Identical input + LOOP AT it_old ASSIGNING . + CLEAR ls_diff. + ls_diff-old_num = sy-tabix. + ls_diff-old = . + READ TABLE it_new ASSIGNING INDEX sy-tabix. + ASSERT sy-subrc = 0. + ls_diff-new_num = sy-tabix. + ls_diff-new = . + APPEND ls_diff TO rt_diff. + ENDLOOP. + ELSE. + ASSERT 0 = 1. " incorrect function call + ENDIF. ENDMETHOD. METHOD constructor. - DATA: lt_delta TYPE vxabapt255_tab, - lt_new TYPE abaptxt255_tab, - lt_old TYPE abaptxt255_tab. + DATA: lt_new TYPE rswsourcet, + lt_old TYPE rswsourcet. + mv_compare_mode = 1. + IF iv_ignore_indentation = abap_true. + mv_compare_mode = mv_compare_mode + 1. + ENDIF. + IF iv_ignore_comments = abap_true. + mv_compare_mode = mv_compare_mode + 2. + ENDIF. + mv_ignore_case = iv_ignore_case. unpack( EXPORTING iv_new = iv_new iv_old = iv_old IMPORTING et_new = lt_new et_old = lt_old ). - lt_delta = compute( it_new = lt_new - it_old = lt_old ). + mt_diff = compute_and_render( it_new = lt_new + it_old = lt_old ). - mt_diff = render( it_new = lt_new - it_old = lt_old - it_delta = lt_delta ). - - calculate_line_num_and_stats( ). + calculate_stats( ). map_beacons( ). shortlist( ). @@ -272,75 +304,6 @@ CLASS zcl_abapgit_diff IMPLEMENTATION. ENDMETHOD. - METHOD render. - - DATA: lv_oindex TYPE i VALUE 1, - lv_nindex TYPE i VALUE 1, - ls_new LIKE LINE OF it_new, - ls_old LIKE LINE OF it_old, - ls_diff LIKE LINE OF rt_diff, - lt_delta LIKE it_delta, - ls_delta LIKE LINE OF it_delta. - - - lt_delta = it_delta. - - DO. - READ TABLE lt_delta INTO ls_delta WITH KEY number = lv_oindex. - IF sy-subrc = 0. - DELETE lt_delta INDEX sy-tabix. - - CASE ls_delta-vrsflag. - WHEN zif_abapgit_definitions=>c_diff-delete. - ls_diff-new = ''. - ls_diff-result = zif_abapgit_definitions=>c_diff-delete. - ls_diff-old = ls_delta-line. - - lv_oindex = lv_oindex + 1. - WHEN zif_abapgit_definitions=>c_diff-insert. - ls_diff-new = ls_delta-line. - ls_diff-result = zif_abapgit_definitions=>c_diff-insert. - ls_diff-old = ''. - - lv_nindex = lv_nindex + 1. - WHEN zif_abapgit_definitions=>c_diff-update. - CLEAR ls_new. - READ TABLE it_new INTO ls_new INDEX lv_nindex. - ASSERT sy-subrc = 0. - - ls_diff-new = ls_new. - ls_diff-result = zif_abapgit_definitions=>c_diff-update. - ls_diff-old = ls_delta-line. - - lv_nindex = lv_nindex + 1. - lv_oindex = lv_oindex + 1. - WHEN OTHERS. - ASSERT 0 = 1. - ENDCASE. - ELSE. - CLEAR ls_new. - READ TABLE it_new INTO ls_new INDEX lv_nindex. "#EC CI_SUBRC - lv_nindex = lv_nindex + 1. - CLEAR ls_old. - READ TABLE it_old INTO ls_old INDEX lv_oindex. "#EC CI_SUBRC - lv_oindex = lv_oindex + 1. - - ls_diff-new = ls_new. - ls_diff-result = ''. - ls_diff-old = ls_old. - ENDIF. - - APPEND ls_diff TO rt_diff. - CLEAR ls_diff. - - IF lv_nindex > lines( it_new ) AND lv_oindex > lines( it_old ). - EXIT. - ENDIF. - ENDDO. - - ENDMETHOD. - - METHOD set_patch_by_old_diff. FIELD-SYMBOLS: TYPE zif_abapgit_definitions=>ty_diff. diff --git a/src/utils/zcl_abapgit_diff.clas.testclasses.abap b/src/utils/zcl_abapgit_diff.clas.testclasses.abap index 422416a12..da0bd680f 100644 --- a/src/utils/zcl_abapgit_diff.clas.testclasses.abap +++ b/src/utils/zcl_abapgit_diff.clas.testclasses.abap @@ -18,7 +18,11 @@ CLASS ltcl_diff DEFINITION FOR TESTING iv_old TYPE zif_abapgit_definitions=>ty_diff-old. METHODS: setup. - METHODS: test. + METHODS: test + IMPORTING + !iv_ignore_indentation TYPE abap_bool DEFAULT abap_false + !iv_ignore_comments TYPE abap_bool DEFAULT abap_false + !iv_ignore_case TYPE abap_bool DEFAULT abap_false. METHODS: diff01 FOR TESTING, @@ -26,7 +30,10 @@ CLASS ltcl_diff DEFINITION FOR TESTING diff03 FOR TESTING, diff04 FOR TESTING, diff05 FOR TESTING, - diff06 FOR TESTING. + diff06 FOR TESTING, + diff07 FOR TESTING, + diff08 FOR TESTING, + diff09 FOR TESTING. ENDCLASS. @@ -85,8 +92,12 @@ CLASS ltcl_diff IMPLEMENTATION. CREATE OBJECT lo_diff EXPORTING - iv_new = lv_xnew - iv_old = lv_xold. + iv_new = lv_xnew + iv_old = lv_xold + iv_ignore_indentation = iv_ignore_indentation + iv_ignore_comments = iv_ignore_comments + iv_ignore_case = iv_ignore_case. + lt_diff = lo_diff->get( ). @@ -183,7 +194,7 @@ CLASS ltcl_diff IMPLEMENTATION. METHOD diff06. - + " mixed add_new( iv_new = 'A' ). add_new( iv_new = 'B' ). add_new( iv_new = 'inserted' ). @@ -225,4 +236,118 @@ CLASS ltcl_diff IMPLEMENTATION. ENDMETHOD. + METHOD diff07. + + " ignore indentation + add_new( iv_new = 'A' ). + add_new( iv_new = ' B' ). " changed indent + add_new( iv_new = 'C' ). + add_new( iv_new = ' D' ). " changed indent + + add_old( iv_old = 'A' ). + add_old( iv_old = 'B' ). + add_old( iv_old = 'C' ). + add_old( iv_old = 'D' ). + + add_expected( iv_new_num = ' 1' + iv_new = 'A' + iv_result = '' + iv_old_num = ' 1' + iv_old = 'A' ). + add_expected( iv_new_num = ' 2' + iv_new = ' B' + iv_result = '' " no diff! + iv_old_num = ' 2' + iv_old = 'B' ). + add_expected( iv_new_num = ' 3' + iv_new = 'C' + iv_result = '' + iv_old_num = ' 3' + iv_old = 'C' ). + add_expected( iv_new_num = ' 4' + iv_new = ' D' + iv_result = '' " no diff! + iv_old_num = ' 4' + iv_old = 'D' ). + + test( iv_ignore_indentation = abap_true ). + + ENDMETHOD. + + METHOD diff08. + + " ignore comments + add_new( iv_new = 'A' ). + add_new( iv_new = '* X' ). " changed comment + add_new( iv_new = 'C' ). + add_new( iv_new = 'D " new' ). " changed comment + + add_old( iv_old = 'A' ). + add_old( iv_old = '* B' ). + add_old( iv_old = 'C' ). + add_old( iv_old = 'D " old' ). + + add_expected( iv_new_num = ' 1' + iv_new = 'A' + iv_result = '' + iv_old_num = ' 1' + iv_old = 'A' ). + add_expected( iv_new_num = ' 2' + iv_new = '* X' + iv_result = '' " no diff! + iv_old_num = ' 2' + iv_old = '* B' ). + add_expected( iv_new_num = ' 3' + iv_new = 'C' + iv_result = '' + iv_old_num = ' 3' + iv_old = 'C' ). + add_expected( iv_new_num = ' 4' + iv_new = 'D " new' + iv_result = '' " no diff! + iv_old_num = ' 4' + iv_old = 'D " old' ). + + test( iv_ignore_comments = abap_true ). + + ENDMETHOD. + + METHOD diff09. + + " ignore case + add_new( iv_new = 'A' ). + add_new( iv_new = 'b' ). " changed case + add_new( iv_new = 'c' ). + add_new( iv_new = 'D' ). " changed case + + add_old( iv_old = 'A' ). + add_old( iv_old = 'B' ). + add_old( iv_old = 'c' ). + add_old( iv_old = 'd' ). + + add_expected( iv_new_num = ' 1' + iv_new = 'A' + iv_result = '' + iv_old_num = ' 1' + iv_old = 'A' ). + add_expected( iv_new_num = ' 2' + iv_new = 'b' + iv_result = '' " no diff! + iv_old_num = ' 2' + iv_old = 'B' ). + add_expected( iv_new_num = ' 3' + iv_new = 'c' + iv_result = '' + iv_old_num = ' 3' + iv_old = 'c' ). + add_expected( iv_new_num = ' 4' + iv_new = 'D' + iv_result = '' " no diff! + iv_old_num = ' 4' + iv_old = 'd' ). + + test( iv_ignore_case = abap_true ). + + ENDMETHOD. + ENDCLASS.