CLASS zcl_abapgit_gui_page_diff DEFINITION PUBLIC INHERITING FROM zcl_abapgit_gui_page FINAL CREATE PUBLIC. PUBLIC SECTION. INTERFACES zif_abapgit_gui_page_hotkey. TYPES: BEGIN OF ty_file_diff, path TYPE string, filename TYPE string, obj_type TYPE string, obj_name TYPE string, lstate TYPE char1, rstate TYPE char1, fstate TYPE char1, " FILE state - Abstraction for shorter ifs o_diff TYPE REF TO zcl_abapgit_diff, changed_by TYPE xubname, type TYPE string, END OF ty_file_diff. TYPES: tt_file_diff TYPE STANDARD TABLE OF ty_file_diff. CONSTANTS: BEGIN OF c_fstate, local TYPE char1 VALUE 'L', remote TYPE char1 VALUE 'R', both TYPE char1 VALUE 'B', END OF c_fstate. METHODS constructor IMPORTING !iv_key TYPE zif_abapgit_persistence=>ty_repo-key !is_file TYPE zif_abapgit_definitions=>ty_file OPTIONAL !is_object TYPE zif_abapgit_definitions=>ty_item OPTIONAL !iv_patch_mode TYPE abap_bool DEFAULT abap_false RAISING zcx_abapgit_exception. METHODS zif_abapgit_gui_event_handler~on_event REDEFINITION. PROTECTED SECTION. METHODS: render_content REDEFINITION, scripts REDEFINITION. PRIVATE SECTION. TYPES: ty_patch_action TYPE string. CONSTANTS: BEGIN OF c_actions, stage TYPE string VALUE 'patch_stage', toggle_unified TYPE string VALUE 'toggle_unified', END OF c_actions, BEGIN OF c_patch_action, add TYPE ty_patch_action VALUE 'add', remove TYPE ty_patch_action VALUE 'remove', END OF c_patch_action. DATA: mt_diff_files TYPE tt_file_diff, mt_delayed_lines TYPE zif_abapgit_definitions=>ty_diffs_tt, mv_unified TYPE abap_bool VALUE abap_true, mv_repo_key TYPE zif_abapgit_persistence=>ty_repo-key, mv_seed TYPE string, " Unique page id to bind JS sessionStorage mv_patch_mode TYPE abap_bool, mo_stage TYPE REF TO zcl_abapgit_stage, mv_section_count TYPE i. METHODS render_diff IMPORTING is_diff TYPE ty_file_diff RETURNING VALUE(ro_html) TYPE REF TO zcl_abapgit_html. METHODS render_diff_head IMPORTING is_diff TYPE ty_file_diff RETURNING VALUE(ro_html) TYPE REF TO zcl_abapgit_html. METHODS render_table_head IMPORTING is_diff TYPE ty_file_diff RETURNING VALUE(ro_html) TYPE REF TO zcl_abapgit_html. METHODS render_lines IMPORTING is_diff TYPE ty_file_diff RETURNING VALUE(ro_html) TYPE REF TO zcl_abapgit_html. METHODS render_beacon IMPORTING is_diff_line TYPE zif_abapgit_definitions=>ty_diff is_diff TYPE ty_file_diff RETURNING VALUE(ro_html) TYPE REF TO zcl_abapgit_html. METHODS render_line_split IMPORTING is_diff_line TYPE zif_abapgit_definitions=>ty_diff iv_filename TYPE string iv_fstate TYPE char1 iv_index TYPE sy-tabix RETURNING VALUE(ro_html) TYPE REF TO zcl_abapgit_html. METHODS render_line_unified IMPORTING is_diff_line TYPE zif_abapgit_definitions=>ty_diff OPTIONAL RETURNING VALUE(ro_html) TYPE REF TO zcl_abapgit_html. METHODS append_diff IMPORTING it_remote TYPE zif_abapgit_definitions=>ty_files_tt it_local TYPE zif_abapgit_definitions=>ty_files_item_tt is_status TYPE zif_abapgit_definitions=>ty_result RAISING zcx_abapgit_exception. METHODS build_menu RETURNING VALUE(ro_menu) TYPE REF TO zcl_abapgit_html_toolbar. METHODS is_binary IMPORTING iv_d1 TYPE xstring iv_d2 TYPE xstring RETURNING VALUE(rv_yes) TYPE abap_bool. METHODS add_to_stage RAISING zcx_abapgit_exception. METHODS render_patch IMPORTING io_html TYPE REF TO zcl_abapgit_html iv_patch_line_possible TYPE abap_bool iv_filename TYPE string is_diff_line TYPE zif_abapgit_definitions=>ty_diff iv_index TYPE sy-tabix. METHODS start_staging IMPORTING it_postdata TYPE cnht_post_data_tab RAISING zcx_abapgit_exception. METHODS apply_patch_all IMPORTING iv_patch TYPE string iv_patch_flag TYPE abap_bool RAISING zcx_abapgit_exception. METHODS render_patch_head IMPORTING io_html TYPE REF TO zcl_abapgit_html is_diff TYPE ty_file_diff. METHODS apply_patch_for IMPORTING iv_filename TYPE string iv_line_index TYPE string iv_patch_flag TYPE abap_bool RAISING zcx_abapgit_exception. METHODS get_diff_object IMPORTING iv_filename TYPE string RETURNING VALUE(ro_diff) TYPE REF TO zcl_abapgit_diff RAISING zcx_abapgit_exception. METHODS get_diff_line IMPORTING io_diff TYPE REF TO zcl_abapgit_diff iv_line_index TYPE string RETURNING VALUE(rs_diff) TYPE zif_abapgit_definitions=>ty_diff RAISING zcx_abapgit_exception. METHODS are_all_lines_patched IMPORTING it_diff TYPE zif_abapgit_definitions=>ty_diffs_tt RETURNING VALUE(rv_are_all_lines_patched) TYPE abap_bool. METHODS add_jump_sub_menu IMPORTING io_menu TYPE REF TO zcl_abapgit_html_toolbar. METHODS add_filter_sub_menu IMPORTING io_menu TYPE REF TO zcl_abapgit_html_toolbar. CLASS-METHODS get_patch_data IMPORTING iv_patch TYPE string EXPORTING ev_filename TYPE string ev_line_index TYPE string RAISING zcx_abapgit_exception. ENDCLASS. CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION. METHOD add_filter_sub_menu. DATA: lo_sub_filter TYPE REF TO zcl_abapgit_html_toolbar, lt_types TYPE string_table, lt_users TYPE string_table. FIELD-SYMBOLS: LIKE LINE OF mt_diff_files, TYPE string. " Get unique LOOP AT mt_diff_files ASSIGNING . APPEND -type TO lt_types. APPEND -changed_by TO lt_users. ENDLOOP. SORT lt_types. DELETE ADJACENT DUPLICATES FROM lt_types. SORT lt_users. DELETE ADJACENT DUPLICATES FROM lt_users. IF lines( lt_types ) > 1 OR lines( lt_users ) > 1. CREATE OBJECT lo_sub_filter EXPORTING iv_id = 'diff-filter'. " File types IF lines( lt_types ) > 1. lo_sub_filter->add( iv_txt = 'TYPE' iv_typ = zif_abapgit_html=>c_action_type-separator ). LOOP AT lt_types ASSIGNING . lo_sub_filter->add( iv_txt = iv_typ = zif_abapgit_html=>c_action_type-onclick iv_aux = 'type' iv_chk = abap_true ). ENDLOOP. ENDIF. " Changed by IF lines( lt_users ) > 1. lo_sub_filter->add( iv_txt = 'CHANGED BY' iv_typ = zif_abapgit_html=>c_action_type-separator ). LOOP AT lt_users ASSIGNING . lo_sub_filter->add( iv_txt = iv_typ = zif_abapgit_html=>c_action_type-onclick iv_aux = 'changed-by' iv_chk = abap_true ). ENDLOOP. ENDIF. io_menu->add( iv_txt = 'Filter' io_sub = lo_sub_filter ) ##NO_TEXT. ENDIF. ENDMETHOD. METHOD add_jump_sub_menu. DATA: lo_sub_jump TYPE REF TO zcl_abapgit_html_toolbar. FIELD-SYMBOLS: LIKE LINE OF mt_diff_files. CREATE OBJECT lo_sub_jump EXPORTING iv_id = 'jump'. LOOP AT mt_diff_files ASSIGNING . lo_sub_jump->add( iv_id = |li_jump_{ sy-tabix }| iv_txt = -filename iv_typ = zif_abapgit_html=>c_action_type-onclick ). ENDLOOP. io_menu->add( iv_txt = 'Jump' io_sub = lo_sub_jump ) ##NO_TEXT. ENDMETHOD. METHOD add_to_stage. DATA: lo_repo TYPE REF TO zcl_abapgit_repo_online, lt_diff TYPE zif_abapgit_definitions=>ty_diffs_tt, lv_something_patched TYPE abap_bool, ls_status TYPE zif_abapgit_definitions=>ty_result, lv_patch TYPE xstring, lo_git_add_patch TYPE REF TO zcl_abapgit_git_add_patch. FIELD-SYMBOLS: TYPE ty_file_diff. lo_repo ?= zcl_abapgit_repo_srv=>get_instance( )->get( mv_repo_key ). LOOP AT mt_diff_files ASSIGNING . IF -o_diff IS NOT BOUND. " When we deal with binary files we don't have a diff object. " There's nothing to do because they cannot be patched CONTINUE. ENDIF. lt_diff = -o_diff->get( ). READ TABLE lt_diff TRANSPORTING NO FIELDS WITH KEY patch_flag = abap_true. CHECK sy-subrc = 0. lv_something_patched = abap_true. CREATE OBJECT lo_git_add_patch EXPORTING it_diff = -o_diff->get( ). lv_patch = lo_git_add_patch->get_patch_binary( ). IF -lstate = 'D' AND are_all_lines_patched( lt_diff ) = abap_true. ls_status-lstate = zif_abapgit_definitions=>c_state-deleted. mo_stage->rm( iv_path = -path is_status = ls_status iv_filename = -filename ). ELSE. IF -lstate = 'A' AND are_all_lines_patched( lt_diff ) = abap_true. ls_status-lstate = zif_abapgit_definitions=>c_state-added. ELSE. ls_status-lstate = zif_abapgit_definitions=>c_state-modified. ENDIF. mo_stage->add( iv_path = -path iv_filename = -filename is_status = ls_status iv_data = lv_patch ). ENDIF. ENDLOOP. IF lv_something_patched = abap_false. zcx_abapgit_exception=>raise( |Nothing added| ). ENDIF. ENDMETHOD. METHOD append_diff. DATA: lv_offs TYPE i, ls_r_dummy LIKE LINE OF it_remote ##NEEDED, ls_l_dummy LIKE LINE OF it_local ##NEEDED. FIELD-SYMBOLS: LIKE LINE OF it_remote, LIKE LINE OF it_local, LIKE LINE OF mt_diff_files. READ TABLE it_remote ASSIGNING WITH KEY filename = is_status-filename path = is_status-path. IF sy-subrc <> 0. ASSIGN ls_r_dummy TO . ENDIF. READ TABLE it_local ASSIGNING WITH KEY file-filename = is_status-filename file-path = is_status-path. IF sy-subrc <> 0. ASSIGN ls_l_dummy TO . ENDIF. IF IS INITIAL AND IS INITIAL. zcx_abapgit_exception=>raise( |DIFF: file not found { is_status-filename }| ). ENDIF. APPEND INITIAL LINE TO mt_diff_files ASSIGNING . -path = is_status-path. -filename = is_status-filename. -obj_type = is_status-obj_type. -obj_name = is_status-obj_name. -lstate = is_status-lstate. -rstate = is_status-rstate. IF -lstate IS NOT INITIAL AND -rstate IS NOT INITIAL. -fstate = c_fstate-both. ELSEIF -lstate IS NOT INITIAL. -fstate = c_fstate-local. ELSE. "rstate IS NOT INITIAL, lstate = empty. -fstate = c_fstate-remote. ENDIF. " Changed by IF -item-obj_type IS NOT INITIAL. -changed_by = to_lower( zcl_abapgit_objects=>changed_by( -item ) ). ENDIF. " Extension IF -file-filename IS NOT INITIAL. -type = reverse( -file-filename ). ELSE. -type = reverse( -filename ). ENDIF. FIND FIRST OCCURRENCE OF '.' IN -type MATCH OFFSET lv_offs. -type = reverse( substring( val = -type len = lv_offs ) ). IF -type <> 'xml' AND -type <> 'abap'. -type = 'other'. ENDIF. IF -type = 'other' AND is_binary( iv_d1 = -data iv_d2 = -file-data ) = abap_true. -type = 'binary'. ENDIF. " Diff data IF -type <> 'binary'. IF -fstate = c_fstate-remote. " Remote file leading changes CREATE OBJECT -o_diff EXPORTING iv_new = -data iv_old = -file-data. ELSE. " Local leading changes or both were modified CREATE OBJECT -o_diff EXPORTING iv_new = -file-data iv_old = -data. ENDIF. ENDIF. ENDMETHOD. METHOD apply_patch_all. DATA: lv_filename TYPE string, lt_patch TYPE string_table, lv_line_index TYPE string. FIELD-SYMBOLS: TYPE LINE OF string_table. SPLIT iv_patch AT ',' INTO TABLE lt_patch. LOOP AT lt_patch ASSIGNING . get_patch_data( EXPORTING iv_patch = IMPORTING ev_filename = lv_filename ev_line_index = lv_line_index ). apply_patch_for( iv_filename = lv_filename iv_line_index = lv_line_index iv_patch_flag = iv_patch_flag ). ENDLOOP. ENDMETHOD. METHOD apply_patch_for. DATA: lo_diff TYPE REF TO zcl_abapgit_diff, ls_diff_line TYPE zif_abapgit_definitions=>ty_diff, lv_line TYPE i. lo_diff = get_diff_object( iv_filename ). ls_diff_line = get_diff_line( io_diff = lo_diff iv_line_index = iv_line_index ). CASE ls_diff_line-result. WHEN zif_abapgit_definitions=>c_diff-update OR zif_abapgit_definitions=>c_diff-insert. lv_line = ls_diff_line-new_num. lo_diff->set_patch_new( iv_line_new = lv_line iv_patch_flag = iv_patch_flag ). WHEN zif_abapgit_definitions=>c_diff-delete. lv_line = ls_diff_line-old_num. lo_diff->set_patch_old( iv_line_old = lv_line iv_patch_flag = iv_patch_flag ). ENDCASE. ENDMETHOD. METHOD are_all_lines_patched. DATA: lv_patch_count TYPE i. FIELD-SYMBOLS: TYPE zif_abapgit_definitions=>ty_diff. LOOP AT it_diff ASSIGNING WHERE patch_flag = abap_true. lv_patch_count = lv_patch_count + 1. ENDLOOP. rv_are_all_lines_patched = boolc( lv_patch_count = lines( it_diff ) ). ENDMETHOD. METHOD build_menu. CREATE OBJECT ro_menu. add_jump_sub_menu( ro_menu ). add_filter_sub_menu( ro_menu ). IF mv_patch_mode = abap_true. ro_menu->add( iv_txt = 'Stage' iv_act = c_actions-stage iv_id = 'stage' iv_typ = zif_abapgit_html=>c_action_type-dummy ) ##NO_TEXT. ELSE. ro_menu->add( iv_txt = 'Split/Unified view' iv_act = c_actions-toggle_unified ) ##NO_TEXT. ENDIF. ENDMETHOD. METHOD constructor. DATA: lt_remote TYPE zif_abapgit_definitions=>ty_files_tt, lt_local TYPE zif_abapgit_definitions=>ty_files_item_tt, lt_status TYPE zif_abapgit_definitions=>ty_results_tt, lo_repo TYPE REF TO zcl_abapgit_repo, lv_ts TYPE timestamp. FIELD-SYMBOLS: LIKE LINE OF lt_status. super->constructor( ). ms_control-page_title = 'DIFF'. mv_unified = zcl_abapgit_persistence_user=>get_instance( )->get_diff_unified( ). mv_repo_key = iv_key. mv_patch_mode = iv_patch_mode. IF mv_patch_mode = abap_true. " While patching we always want to be in split mode CLEAR: mv_unified. CREATE OBJECT mo_stage. ENDIF. GET TIME STAMP FIELD lv_ts. mv_seed = |diff{ lv_ts }|. " Generate based on time ASSERT is_file IS INITIAL OR is_object IS INITIAL. " just one passed lo_repo = zcl_abapgit_repo_srv=>get_instance( )->get( iv_key ). lt_remote = lo_repo->get_files_remote( ). lt_local = lo_repo->get_files_local( ). lt_status = lo_repo->status( ). IF is_file IS NOT INITIAL. " Diff for one file READ TABLE lt_status ASSIGNING WITH KEY path = is_file-path filename = is_file-filename. append_diff( it_remote = lt_remote it_local = lt_local is_status = ). ELSEIF is_object IS NOT INITIAL. " Diff for whole object LOOP AT lt_status ASSIGNING WHERE obj_type = is_object-obj_type AND obj_name = is_object-obj_name AND match IS INITIAL. append_diff( it_remote = lt_remote it_local = lt_local is_status = ). ENDLOOP. ELSE. " Diff for the whole repo SORT lt_status BY path ASCENDING filename ASCENDING. LOOP AT lt_status ASSIGNING WHERE match IS INITIAL. append_diff( it_remote = lt_remote it_local = lt_local is_status = ). ENDLOOP. ENDIF. IF lines( mt_diff_files ) = 0. zcx_abapgit_exception=>raise( 'PAGE_DIFF ERROR: No diff files found' ). ENDIF. ms_control-page_menu = build_menu( ). ENDMETHOD. METHOD get_diff_line. DATA: lt_diff TYPE zif_abapgit_definitions=>ty_diffs_tt, lv_line_index TYPE sy-tabix. lv_line_index = iv_line_index. lt_diff = io_diff->get( ). READ TABLE lt_diff INTO rs_diff INDEX lv_line_index. IF sy-subrc <> 0. zcx_abapgit_exception=>raise( |Invalid line index { lv_line_index }| ). ENDIF. ENDMETHOD. METHOD get_diff_object. FIELD-SYMBOLS: LIKE LINE OF mt_diff_files. READ TABLE mt_diff_files ASSIGNING WITH KEY filename = iv_filename. IF sy-subrc <> 0. zcx_abapgit_exception=>raise( |Invalid filename { iv_filename }| ). ENDIF. ro_diff = -o_diff. ENDMETHOD. METHOD get_patch_data. DATA: lv_section TYPE string. CLEAR: ev_filename, ev_line_index. FIND FIRST OCCURRENCE OF REGEX `patch_line` && `_(.*)_(\d)+_(\d+)` IN iv_patch SUBMATCHES ev_filename lv_section ev_line_index. IF sy-subrc <> 0. zcx_abapgit_exception=>raise( |Invalid patch| ). ENDIF. ENDMETHOD. METHOD is_binary. FIELD-SYMBOLS LIKE iv_d1. IF iv_d1 IS NOT INITIAL. " One of them might be new and so empty ASSIGN iv_d1 TO . ELSE. ASSIGN iv_d2 TO . ENDIF. rv_yes = zcl_abapgit_utils=>is_binary( ). ENDMETHOD. METHOD render_beacon. DATA: lv_beacon TYPE string, lt_beacons TYPE zif_abapgit_definitions=>ty_string_tt. CREATE OBJECT ro_html. mv_section_count = mv_section_count + 1. IF is_diff_line-beacon > 0. lt_beacons = is_diff-o_diff->get_beacons( ). READ TABLE lt_beacons INTO lv_beacon INDEX is_diff_line-beacon. ELSE. lv_beacon = '---'. ENDIF. ro_html->add( '' ). ro_html->add( '' ). IF mv_patch_mode = abap_true. ro_html->add( || ). ro_html->add_checkbox( iv_id = |patch_section_{ is_diff-filename }_{ mv_section_count }| ). ro_html->add( '' ). ELSE. ro_html->add( '' ). ENDIF. IF mv_unified = abap_true. ro_html->add( '' ). ro_html->add( '' ). ro_html->add( |@@ { is_diff_line-new_num } @@ { lv_beacon }| ). ELSE. ro_html->add( |@@ { is_diff_line-new_num } @@ { lv_beacon }| ). ENDIF. ro_html->add( '' ). ro_html->add( '' ). ENDMETHOD. METHOD render_content. DATA: ls_diff_file LIKE LINE OF mt_diff_files, li_progress TYPE REF TO zif_abapgit_progress. CREATE OBJECT ro_html. CLEAR: mv_section_count. li_progress = zcl_abapgit_progress=>get_instance( lines( mt_diff_files ) ). ro_html->add( |
| ). ro_html->add( zcl_abapgit_gui_chunk_lib=>render_js_error_banner( ) ). LOOP AT mt_diff_files INTO ls_diff_file. li_progress->show( iv_current = sy-tabix iv_text = |Render Diff - { ls_diff_file-filename }| ). ro_html->add( render_diff( ls_diff_file ) ). ENDLOOP. ro_html->add( '
' ). ENDMETHOD. METHOD render_diff. CREATE OBJECT ro_html. ro_html->add( |
| ). "#EC NOTEXT ro_html->add( render_diff_head( is_diff ) ). " Content IF is_diff-type <> 'binary'. ro_html->add( '
' ). "#EC NOTEXT ro_html->add( || ). "#EC NOTEXT ro_html->add( render_table_head( is_diff ) ). ro_html->add( render_lines( is_diff ) ). ro_html->add( '
' ). "#EC NOTEXT ELSE. ro_html->add( '
' ). "#EC NOTEXT ro_html->add( 'The content seems to be binary.' ). "#EC NOTEXT ro_html->add( 'Cannot display as diff.' ). "#EC NOTEXT ENDIF. ro_html->add( '
' ). "#EC NOTEXT ro_html->add( '
' ). "#EC NOTEXT ENDMETHOD. METHOD render_diff_head. DATA: ls_stats TYPE zif_abapgit_definitions=>ty_count, lv_adt_link TYPE string. CREATE OBJECT ro_html. ro_html->add( '
' ). "#EC NOTEXT IF is_diff-type <> 'binary'. ls_stats = is_diff-o_diff->stats( ). IF is_diff-fstate = c_fstate-both. " Merge stats into 'update' if both were changed ls_stats-update = ls_stats-update + ls_stats-insert + ls_stats-delete. CLEAR: ls_stats-insert, ls_stats-delete. ENDIF. ro_html->add( |+ { ls_stats-insert }| ). ro_html->add( |- { ls_stats-delete }| ). ro_html->add( |~ { ls_stats-update }| ). ENDIF. " no links for nonexistent or deleted objects IF is_diff-lstate IS NOT INITIAL AND is_diff-lstate <> 'D'. lv_adt_link = zcl_abapgit_html=>a( iv_txt = |{ is_diff-path }{ is_diff-filename }| iv_typ = zif_abapgit_html=>c_action_type-sapevent iv_act = |jump?TYPE={ is_diff-obj_type }&NAME={ is_diff-obj_name }| ). ENDIF. IF lv_adt_link IS NOT INITIAL. ro_html->add( |{ lv_adt_link }| ). "#EC NOTEXT ELSE. ro_html->add( |{ is_diff-path }{ is_diff-filename }| ). "#EC NOTEXT ENDIF. ro_html->add( zcl_abapgit_gui_chunk_lib=>render_item_state( iv_lstate = is_diff-lstate iv_rstate = is_diff-rstate ) ). IF is_diff-fstate = c_fstate-both AND mv_unified = abap_true. ro_html->add( 'Attention: Unified mode' && ' highlighting for MM assumes local file is newer ! ' ). "#EC NOTEXT ENDIF. ro_html->add( |last change by: { is_diff-changed_by }| ). ro_html->add( '
' ). "#EC NOTEXT ENDMETHOD. METHOD render_lines. DATA: lo_highlighter TYPE REF TO zcl_abapgit_syntax_highlighter, lt_diffs TYPE zif_abapgit_definitions=>ty_diffs_tt, lv_insert_nav TYPE abap_bool, lv_tabix TYPE syst-tabix. FIELD-SYMBOLS LIKE LINE OF lt_diffs. lo_highlighter = zcl_abapgit_syntax_highlighter=>create( is_diff-filename ). CREATE OBJECT ro_html. lt_diffs = is_diff-o_diff->get( ). LOOP AT lt_diffs ASSIGNING . lv_tabix = sy-tabix. IF -short = abap_false. lv_insert_nav = abap_true. CONTINUE. ENDIF. IF lv_insert_nav = abap_true. " Insert separator line with navigation ro_html->add( render_beacon( is_diff_line = is_diff = is_diff ) ). lv_insert_nav = abap_false. ENDIF. IF lo_highlighter IS BOUND. -new = lo_highlighter->process_line( -new ). -old = lo_highlighter->process_line( -old ). ELSE. -new = escape( val = -new format = cl_abap_format=>e_html_attr ). -old = escape( val = -old format = cl_abap_format=>e_html_attr ). ENDIF. CONDENSE -new_num. "get rid of leading spaces CONDENSE -old_num. IF mv_unified = abap_true. ro_html->add( render_line_unified( is_diff_line = ) ). ELSE. ro_html->add( render_line_split( is_diff_line = iv_filename = is_diff-filename iv_fstate = is_diff-fstate iv_index = lv_tabix ) ). ENDIF. ENDLOOP. IF mv_unified = abap_true. ro_html->add( render_line_unified( ) ). " Release delayed lines ENDIF. ENDMETHOD. METHOD render_line_split. DATA: lv_new TYPE string, lv_old TYPE string, lv_mark TYPE string, lv_bg TYPE string, lv_patch_line_possible TYPE abap_bool. CREATE OBJECT ro_html. " New line lv_mark = ` `. IF is_diff_line-result IS NOT INITIAL. IF iv_fstate = c_fstate-both OR is_diff_line-result = zif_abapgit_definitions=>c_diff-update. lv_bg = ' diff_upd'. lv_mark = `~`. ELSEIF is_diff_line-result = zif_abapgit_definitions=>c_diff-insert. lv_bg = ' diff_ins'. lv_mark = `+`. ENDIF. ENDIF. lv_new = || && |{ lv_mark }| && |{ is_diff_line-new }|. IF lv_mark <> ` `. lv_patch_line_possible = abap_true. ENDIF. " Old line CLEAR lv_bg. lv_mark = ` `. IF is_diff_line-result IS NOT INITIAL. IF iv_fstate = c_fstate-both OR is_diff_line-result = zif_abapgit_definitions=>c_diff-update. lv_bg = ' diff_upd'. lv_mark = `~`. ELSEIF is_diff_line-result = zif_abapgit_definitions=>c_diff-delete. lv_bg = ' diff_del'. lv_mark = `-`. ENDIF. ENDIF. lv_old = || && |{ lv_mark }| && |{ is_diff_line-old }|. IF lv_mark <> ` `. lv_patch_line_possible = abap_true. ENDIF. " render line, inverse sides if remote is newer ro_html->add( '' ). "#EC NOTEXT IF mv_patch_mode = abap_true. render_patch( io_html = ro_html iv_patch_line_possible = lv_patch_line_possible iv_filename = iv_filename is_diff_line = is_diff_line iv_index = iv_index ). ENDIF. IF iv_fstate = c_fstate-remote. " Remote file leading changes ro_html->add( lv_old ). " local ro_html->add( lv_new ). " remote ELSE. " Local leading changes or both were modified ro_html->add( lv_new ). " local ro_html->add( lv_old ). " remote ENDIF. ro_html->add( '' ). "#EC NOTEXT ENDMETHOD. METHOD render_line_unified. FIELD-SYMBOLS LIKE LINE OF mt_delayed_lines. CREATE OBJECT ro_html. " Release delayed subsequent update lines IF is_diff_line-result <> zif_abapgit_definitions=>c_diff-update. LOOP AT mt_delayed_lines ASSIGNING . ro_html->add( '' ). "#EC NOTEXT ro_html->add( || && || && |-| && |{ -old }| ). ro_html->add( '' ). "#EC NOTEXT ENDLOOP. LOOP AT mt_delayed_lines ASSIGNING . ro_html->add( '' ). "#EC NOTEXT ro_html->add( || && || && |+| && |{ -new }| ). ro_html->add( '' ). "#EC NOTEXT ENDLOOP. CLEAR mt_delayed_lines. ENDIF. ro_html->add( '' ). "#EC NOTEXT CASE is_diff_line-result. WHEN zif_abapgit_definitions=>c_diff-update. APPEND is_diff_line TO mt_delayed_lines. " Delay output of subsequent updates WHEN zif_abapgit_definitions=>c_diff-insert. ro_html->add( || && || && |+| && |{ is_diff_line-new }| ). WHEN zif_abapgit_definitions=>c_diff-delete. ro_html->add( || && || && |-| && |{ is_diff_line-old }| ). WHEN OTHERS. "none ro_html->add( || && || && | | && |{ is_diff_line-old }| ). ENDCASE. ro_html->add( '' ). "#EC NOTEXT ENDMETHOD. METHOD render_patch. CONSTANTS: BEGIN OF c_css_class, patch TYPE string VALUE `patch` ##NO_TEXT, END OF c_css_class. DATA: lv_id TYPE string, lv_object TYPE string. lv_object = iv_filename. IF iv_patch_line_possible = abap_true. lv_id = |{ lv_object }_{ mv_section_count }_{ iv_index }|. io_html->add( || ). io_html->add_checkbox( iv_id = |patch_line_{ lv_id }| ). io_html->add( || ). ELSE. io_html->add( || ). io_html->add( || ). ENDIF. ENDMETHOD. METHOD render_patch_head. io_html->add( || ). io_html->add_checkbox( iv_id = |patch_file_{ is_diff-filename }| ). io_html->add( '' ). ENDMETHOD. METHOD render_table_head. CREATE OBJECT ro_html. ro_html->add( '' ). "#EC NOTEXT ro_html->add( '' ). "#EC NOTEXT IF mv_unified = abap_true. ro_html->add( 'old' ). "#EC NOTEXT ro_html->add( 'new' ). "#EC NOTEXT ro_html->add( '' ). "#EC NOTEXT ro_html->add( 'code' ). "#EC NOTEXT ELSE. IF mv_patch_mode = abap_true. render_patch_head( io_html = ro_html is_diff = is_diff ). ENDIF. ro_html->add( '' ). "#EC NOTEXT ro_html->add( '' ). "#EC NOTEXT ro_html->add( 'LOCAL' ). "#EC NOTEXT ro_html->add( '' ). "#EC NOTEXT ro_html->add( '' ). "#EC NOTEXT ro_html->add( 'REMOTE' ). "#EC NOTEXT ENDIF. ro_html->add( '' ). "#EC NOTEXT ro_html->add( '' ). "#EC NOTEXT ENDMETHOD. METHOD scripts. ro_html = super->scripts( ). ro_html->add( 'var gHelper = new DiffHelper({' ). ro_html->add( | seed: "{ mv_seed }",| ). ro_html->add( ' ids: {' ). ro_html->add( ' jump: "jump",' ). ro_html->add( ' diffList: "diff-list",' ). ro_html->add( ' filterMenu: "diff-filter",' ). ro_html->add( ' }' ). ro_html->add( '});' ). IF mv_patch_mode = abap_true. ro_html->add( 'preparePatch();' ). ro_html->add( 'registerStagePatch();' ). ENDIF. ro_html->add( 'addMarginBottom();' ). ro_html->add( 'var gGoJumpPalette = new CommandPalette(enumerateJumpAllFiles, {' ). ro_html->add( ' toggleKey: "F2",' ). ro_html->add( ' hotkeyDescription: "Jump to file ..."' ). ro_html->add( '});' ). " Feature for selecting ABAP code by column and copy to clipboard ro_html->add( 'var columnSelection = new DiffColumnSelection();' ). ENDMETHOD. METHOD start_staging. DATA: lv_string TYPE string, lt_fields TYPE tihttpnvp, lv_add TYPE string, lv_remove TYPE string. CONCATENATE LINES OF it_postdata INTO lv_string. lt_fields = zcl_abapgit_html_action_utils=>parse_fields( lv_string ). zcl_abapgit_html_action_utils=>get_field( EXPORTING iv_name = c_patch_action-add it_field = lt_fields CHANGING cg_field = lv_add ). zcl_abapgit_html_action_utils=>get_field( EXPORTING iv_name = c_patch_action-remove it_field = lt_fields CHANGING cg_field = lv_remove ). apply_patch_all( iv_patch = lv_add iv_patch_flag = abap_true ). apply_patch_all( iv_patch = lv_remove iv_patch_flag = abap_false ). add_to_stage( ). ENDMETHOD. METHOD zif_abapgit_gui_event_handler~on_event. DATA: lo_repo TYPE REF TO zcl_abapgit_repo_online. CASE iv_action. WHEN c_actions-toggle_unified. " Toggle file diplay mv_unified = zcl_abapgit_persistence_user=>get_instance( )->toggle_diff_unified( ). ev_state = zcl_abapgit_gui=>c_event_state-re_render. WHEN c_actions-stage. start_staging( it_postdata ). lo_repo ?= zcl_abapgit_repo_srv=>get_instance( )->get( mv_repo_key ). CREATE OBJECT ei_page TYPE zcl_abapgit_gui_page_commit EXPORTING io_repo = lo_repo io_stage = mo_stage. ev_state = zcl_abapgit_gui=>c_event_state-new_page. WHEN OTHERS. super->zif_abapgit_gui_event_handler~on_event( EXPORTING iv_action = iv_action iv_prev_page = iv_prev_page iv_getdata = iv_getdata it_postdata = it_postdata IMPORTING ei_page = ei_page ev_state = ev_state ). ENDCASE. ENDMETHOD. METHOD zif_abapgit_gui_page_hotkey~get_hotkey_actions. DATA: ls_hotkey_action LIKE LINE OF rt_hotkey_actions. ls_hotkey_action-name = |Stage changes|. ls_hotkey_action-action = |stagePatch|. ls_hotkey_action-hotkey = |s|. INSERT ls_hotkey_action INTO TABLE rt_hotkey_actions. ENDMETHOD. ENDCLASS.