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 <larshp@hotmail.com>
This commit is contained in:
Marc Bernard 2021-10-25 10:06:27 -04:00 committed by GitHub
parent 45806c5a95
commit c15b58400a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 237 additions and 149 deletions

View File

@ -1,37 +1,39 @@
CLASS zcl_abapgit_diff DEFINITION CLASS zcl_abapgit_diff DEFINITION
PUBLIC PUBLIC
CREATE PUBLIC . CREATE PUBLIC.
PUBLIC SECTION. PUBLIC SECTION.
CONSTANTS co_starting_beacon TYPE i VALUE 1. CONSTANTS co_starting_beacon TYPE i VALUE 1.
* assumes data is UTF8 based with newlines * assumes data is UTF8 based with newlines
* only works with lines up to 255 characters
METHODS constructor METHODS constructor
IMPORTING IMPORTING
!iv_new TYPE xstring !iv_new TYPE xstring
!iv_old 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 METHODS get
RETURNING RETURNING
VALUE(rt_diff) TYPE zif_abapgit_definitions=>ty_diffs_tt . VALUE(rt_diff) TYPE zif_abapgit_definitions=>ty_diffs_tt.
METHODS stats METHODS stats
RETURNING RETURNING
VALUE(rs_count) TYPE zif_abapgit_definitions=>ty_count . VALUE(rs_count) TYPE zif_abapgit_definitions=>ty_count.
METHODS set_patch_new METHODS set_patch_new
IMPORTING IMPORTING
!iv_line_new TYPE i !iv_line_new TYPE i
!iv_patch_flag TYPE abap_bool !iv_patch_flag TYPE abap_bool
RAISING RAISING
zcx_abapgit_exception . zcx_abapgit_exception.
METHODS set_patch_old METHODS set_patch_old
IMPORTING IMPORTING
!iv_line_old TYPE i !iv_line_old TYPE i
!iv_patch_flag TYPE abap_bool !iv_patch_flag TYPE abap_bool
RAISING RAISING
zcx_abapgit_exception . zcx_abapgit_exception.
METHODS get_beacons METHODS get_beacons
RETURNING 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 METHODS is_line_patched
IMPORTING IMPORTING
iv_index TYPE i iv_index TYPE i
@ -47,38 +49,35 @@ CLASS zcl_abapgit_diff DEFINITION
PROTECTED SECTION. PROTECTED SECTION.
PRIVATE 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 . TYPES:
DATA mt_diff TYPE zif_abapgit_definitions=>ty_diffs_tt . ty_regexset_tt TYPE STANDARD TABLE OF REF TO cl_abap_regex WITH KEY table_line.
DATA ms_stats TYPE zif_abapgit_definitions=>ty_count .
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 IMPORTING
!iv_new TYPE xstring !iv_new TYPE xstring
!iv_old TYPE xstring !iv_old TYPE xstring
EXPORTING EXPORTING
!et_new TYPE abaptxt255_tab !et_new TYPE rswsourcet
!et_old TYPE abaptxt255_tab . !et_old TYPE rswsourcet.
CLASS-METHODS render METHODS map_beacons.
IMPORTING METHODS shortlist.
!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 .
METHODS create_regex_set METHODS create_regex_set
RETURNING RETURNING
VALUE(rt_regex_set) TYPE ty_regexset_tt. 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. ENDCLASS.
@ -86,31 +85,12 @@ ENDCLASS.
CLASS zcl_abapgit_diff IMPLEMENTATION. CLASS zcl_abapgit_diff IMPLEMENTATION.
METHOD calculate_line_num_and_stats. METHOD calculate_stats.
DATA: lv_new TYPE i VALUE 1,
lv_old TYPE i VALUE 1.
FIELD-SYMBOLS: <ls_diff> LIKE LINE OF mt_diff. FIELD-SYMBOLS: <ls_diff> LIKE LINE OF mt_diff.
LOOP AT mt_diff ASSIGNING <ls_diff>. LOOP AT mt_diff ASSIGNING <ls_diff>.
<ls_diff>-new_num = lv_new. CASE <ls_diff>-result.
<ls_diff>-old_num = lv_old.
CASE <ls_diff>-result. " Line nums
WHEN zif_abapgit_definitions=>c_diff-delete.
lv_old = lv_old + 1.
CLEAR <ls_diff>-new_num.
WHEN zif_abapgit_definitions=>c_diff-insert.
lv_new = lv_new + 1.
CLEAR <ls_diff>-old_num.
WHEN OTHERS.
lv_new = lv_new + 1.
lv_old = lv_old + 1.
ENDCASE.
CASE <ls_diff>-result. " Stats
WHEN zif_abapgit_definitions=>c_diff-insert. WHEN zif_abapgit_definitions=>c_diff-insert.
ms_stats-insert = ms_stats-insert + 1. ms_stats-insert = ms_stats-insert + 1.
WHEN zif_abapgit_definitions=>c_diff-delete. WHEN zif_abapgit_definitions=>c_diff-delete.
@ -118,51 +98,103 @@ CLASS zcl_abapgit_diff IMPLEMENTATION.
WHEN zif_abapgit_definitions=>c_diff-update. WHEN zif_abapgit_definitions=>c_diff-update.
ms_stats-update = ms_stats-update + 1. ms_stats-update = ms_stats-update + 1.
ENDCASE. ENDCASE.
ENDLOOP. ENDLOOP.
ENDMETHOD. ENDMETHOD.
METHOD compute. METHOD compute_and_render.
DATA: lt_trdirtab_old TYPE TABLE OF trdir, DATA:
lt_trdirtab_new TYPE TABLE OF trdir, lv_i TYPE i,
lt_trdir_delta TYPE TABLE OF xtrdir. ls_diff LIKE LINE OF rt_diff,
lt_delta TYPE STANDARD TABLE OF rsedcresul WITH DEFAULT KEY.
FIELD-SYMBOLS:
<ls_old> LIKE LINE OF it_old,
<ls_new> LIKE LINE OF it_new,
<ls_delta> 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 TABLES
texttab_old = it_old text_tab1 = it_new
texttab_new = it_new text_tab2 = it_old
trdirtab_old = lt_trdirtab_old text_tab_res = lt_delta
trdirtab_new = lt_trdirtab_new EXCEPTIONS
trdir_delta = lt_trdir_delta parameter_invalid = 1
text_delta = rt_delta. difference_not_found = 2
OTHERS = 3.
IF sy-subrc = 0.
" Process delta
LOOP AT lt_delta ASSIGNING <ls_delta>.
CLEAR ls_diff.
IF <ls_delta>-line1 > 0.
ls_diff-old_num = lv_i = <ls_delta>-line1.
ls_diff-old = <ls_delta>-text1.
ENDIF.
IF <ls_delta>-line2 > 0.
ls_diff-new_num = lv_i = <ls_delta>-line2.
ls_diff-new = <ls_delta>-text2.
ENDIF.
IF <ls_delta>-flag1 = 'D'.
ls_diff-result = zif_abapgit_definitions=>c_diff-delete.
ELSEIF <ls_delta>-flag2 = 'I'.
ls_diff-result = zif_abapgit_definitions=>c_diff-insert.
ELSEIF <ls_delta>-flag1 = 'M' AND <ls_delta>-flag2 = 'M'.
ls_diff-result = zif_abapgit_definitions=>c_diff-update.
ELSEIF <ls_delta>-flag1 = '' AND <ls_delta>-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 <ls_old>.
CLEAR ls_diff.
ls_diff-old_num = sy-tabix.
ls_diff-old = <ls_old>.
READ TABLE it_new ASSIGNING <ls_new> INDEX sy-tabix.
ASSERT sy-subrc = 0.
ls_diff-new_num = sy-tabix.
ls_diff-new = <ls_new>.
APPEND ls_diff TO rt_diff.
ENDLOOP.
ELSE.
ASSERT 0 = 1. " incorrect function call
ENDIF.
ENDMETHOD. ENDMETHOD.
METHOD constructor. METHOD constructor.
DATA: lt_delta TYPE vxabapt255_tab, DATA: lt_new TYPE rswsourcet,
lt_new TYPE abaptxt255_tab, lt_old TYPE rswsourcet.
lt_old TYPE abaptxt255_tab.
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 unpack( EXPORTING iv_new = iv_new
iv_old = iv_old iv_old = iv_old
IMPORTING et_new = lt_new IMPORTING et_new = lt_new
et_old = lt_old ). et_old = lt_old ).
lt_delta = compute( it_new = lt_new mt_diff = compute_and_render( it_new = lt_new
it_old = lt_old ). it_old = lt_old ).
mt_diff = render( it_new = lt_new calculate_stats( ).
it_old = lt_old
it_delta = lt_delta ).
calculate_line_num_and_stats( ).
map_beacons( ). map_beacons( ).
shortlist( ). shortlist( ).
@ -272,75 +304,6 @@ CLASS zcl_abapgit_diff IMPLEMENTATION.
ENDMETHOD. 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. METHOD set_patch_by_old_diff.
FIELD-SYMBOLS: <ls_diff> TYPE zif_abapgit_definitions=>ty_diff. FIELD-SYMBOLS: <ls_diff> TYPE zif_abapgit_definitions=>ty_diff.

View File

@ -18,7 +18,11 @@ CLASS ltcl_diff DEFINITION FOR TESTING
iv_old TYPE zif_abapgit_definitions=>ty_diff-old. iv_old TYPE zif_abapgit_definitions=>ty_diff-old.
METHODS: setup. 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: METHODS:
diff01 FOR TESTING, diff01 FOR TESTING,
@ -26,7 +30,10 @@ CLASS ltcl_diff DEFINITION FOR TESTING
diff03 FOR TESTING, diff03 FOR TESTING,
diff04 FOR TESTING, diff04 FOR TESTING,
diff05 FOR TESTING, diff05 FOR TESTING,
diff06 FOR TESTING. diff06 FOR TESTING,
diff07 FOR TESTING,
diff08 FOR TESTING,
diff09 FOR TESTING.
ENDCLASS. ENDCLASS.
@ -85,8 +92,12 @@ CLASS ltcl_diff IMPLEMENTATION.
CREATE OBJECT lo_diff CREATE OBJECT lo_diff
EXPORTING EXPORTING
iv_new = lv_xnew iv_new = lv_xnew
iv_old = lv_xold. 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( ). lt_diff = lo_diff->get( ).
@ -183,7 +194,7 @@ CLASS ltcl_diff IMPLEMENTATION.
METHOD diff06. METHOD diff06.
" mixed
add_new( iv_new = 'A' ). add_new( iv_new = 'A' ).
add_new( iv_new = 'B' ). add_new( iv_new = 'B' ).
add_new( iv_new = 'inserted' ). add_new( iv_new = 'inserted' ).
@ -225,4 +236,118 @@ CLASS ltcl_diff IMPLEMENTATION.
ENDMETHOD. 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. ENDCLASS.