diff --git a/src/zabapgit_css_common.w3mi.data.css b/src/zabapgit_css_common.w3mi.data.css index 80fd4ebf1..8db14ac39 100644 --- a/src/zabapgit_css_common.w3mi.data.css +++ b/src/zabapgit_css_common.w3mi.data.css @@ -37,7 +37,7 @@ form input:focus, textarea:focus { .grey { color: lightgrey !important; } .darkgrey { color: #808080 !important; } .attention { color: red !important; } -.blue { color: #5e8dc9; !important; } +.blue { color: #5e8dc9 !important; } .red { color: red !important; } /* MODIFIERS */ @@ -415,8 +415,8 @@ span.diff_banner { padding-right: 0.3em; } .diff_ins { - border-color: #7bea7b; - background-color: #d3f8d3; + border-color: #abf2ab; + background-color: #e0ffe0; } .diff_del { border-color: #ff667d; @@ -500,21 +500,37 @@ table.diff_tab thead.nav_line th { color: #bbb; } table.diff_tab td.num, th.num { - text-align: right; + width: 1%; + min-width: 2em; + padding-right: 8px; + padding-left: 8px; + text-align: right !important; color: #ccc; border-left: 1px solid #eee; border-right: 1px solid #eee; + -ms-user-select: none; + user-select: none; +} +table.diff_tab td.num::before { + content: attr(line-num); } table.diff_tab code { font-family: inherit; white-space: pre; } +table.diff_tab td.code { + /* font-family: inherit; */ + /* white-space: pre; */ + word-wrap: break-word; + white-space: pre-wrap; + overflow: visible; +} table.diff_tab code span.keyword { color: #0a69ce; } table.diff_tab code span.text { color: #48ce4f; } table.diff_tab code span.comment { color: #808080; font-style: italic; } -table.diff_tab code span.xml_tag { color: #3370e0; } -table.diff_tab code span.attr { color: #f20707; } +table.diff_tab code span.xml_tag { color: #457ce3; } +table.diff_tab code span.attr { color: #b777fb; } table.diff_tab code span.attr_val { color: #7a02f9; } table.diff_tab tbody tr:first-child td { padding-top: 0.5em; } diff --git a/src/zabapgit_page_diff.prog.abap b/src/zabapgit_page_diff.prog.abap index c67ed6523..c00b09add 100644 --- a/src/zabapgit_page_diff.prog.abap +++ b/src/zabapgit_page_diff.prog.abap @@ -7,32 +7,40 @@ CLASS lcl_gui_page_diff DEFINITION FINAL INHERITING FROM lcl_gui_page. PUBLIC SECTION. CONSTANTS: - BEGIN OF c_mod, + BEGIN OF c_fstate, local TYPE char1 VALUE 'L', remote TYPE char1 VALUE 'R', both TYPE char1 VALUE 'B', - END OF c_mod. + END OF c_fstate. TYPES: BEGIN OF ty_file_diff, filename TYPE string, lstate TYPE char1, rstate TYPE char1, - mod TYPE char1, " Abstraction for shorter ifs + fstate TYPE char1, " FILE state - Abstraction for shorter ifs o_diff TYPE REF TO lcl_diff, END OF ty_file_diff, tt_file_diff TYPE STANDARD TABLE OF ty_file_diff. - METHODS: constructor - IMPORTING iv_key TYPE lcl_persistence_repo=>ty_repo-key - is_file TYPE ty_file OPTIONAL - is_object TYPE ty_item OPTIONAL - RAISING lcx_exception. + METHODS: + constructor + IMPORTING iv_key TYPE lcl_persistence_repo=>ty_repo-key + is_file TYPE ty_file OPTIONAL + is_object TYPE ty_item OPTIONAL + RAISING lcx_exception, + lif_gui_page~on_event REDEFINITION. PROTECTED SECTION. METHODS render_content REDEFINITION. PRIVATE SECTION. - DATA: mt_diff_files TYPE tt_file_diff. + CONSTANTS: BEGIN OF c_actions, + toggle_unified TYPE string VALUE 'toggle_unified', + END OF c_actions. + + DATA: mt_diff_files TYPE tt_file_diff, + mt_delayed_lines TYPE lcl_diff=>ty_diffs_tt, + mv_unified TYPE abap_bool VALUE abap_true. METHODS render_diff IMPORTING is_diff TYPE ty_file_diff @@ -49,16 +57,20 @@ CLASS lcl_gui_page_diff DEFINITION FINAL INHERITING FROM lcl_gui_page. IMPORTING is_diff_line TYPE lcl_diff=>ty_diff is_diff TYPE ty_file_diff RETURNING VALUE(ro_html) TYPE REF TO lcl_html. - METHODS get_line_hl - IMPORTING iv_mod TYPE char1 - iv_result TYPE lcl_diff=>ty_diff-result - EXPORTING ev_lattr TYPE string - ev_rattr TYPE string. + METHODS render_line_split + IMPORTING is_diff_line TYPE lcl_diff=>ty_diff + iv_fstate TYPE char1 + RETURNING VALUE(ro_html) TYPE REF TO lcl_html. + METHODS render_line_unified + IMPORTING is_diff_line TYPE lcl_diff=>ty_diff OPTIONAL + RETURNING VALUE(ro_html) TYPE REF TO lcl_html. METHODS append_diff IMPORTING it_remote TYPE ty_files_tt it_local TYPE ty_files_item_tt is_status TYPE ty_result RAISING lcx_exception. + METHODS build_menu + RETURNING VALUE(ro_menu) TYPE REF TO lcl_html_toolbar. ENDCLASS. "lcl_gui_page_diff @@ -75,6 +87,8 @@ CLASS lcl_gui_page_diff IMPLEMENTATION. super->constructor( ). ms_control-page_title = 'DIFF'. + ms_control-page_menu = build_menu( ). + mv_unified = lcl_app=>user( )->get_diff_unified( ). ASSERT is_file IS INITIAL OR is_object IS INITIAL. " just one passed @@ -154,14 +168,14 @@ CLASS lcl_gui_page_diff IMPLEMENTATION. -rstate = is_status-rstate. IF -lstate IS NOT INITIAL AND -rstate IS NOT INITIAL. - -mod = c_mod-both. + -fstate = c_fstate-both. ELSEIF -lstate IS NOT INITIAL. - -mod = c_mod-local. + -fstate = c_fstate-local. ELSE. "rstate IS NOT INITIAL, lstate = empty. - -mod = c_mod-remote. + -fstate = c_fstate-remote. ENDIF. - IF -mod = c_mod-remote. " Remote file leading changes + IF -fstate = c_fstate-remote. " Remote file leading changes CREATE OBJECT -o_diff EXPORTING iv_new = -data @@ -175,45 +189,46 @@ CLASS lcl_gui_page_diff IMPLEMENTATION. ENDMETHOD. "append_diff - METHOD render_diff_head. - DATA: lo_html TYPE REF TO lcl_html, - ls_stats TYPE lcl_diff=>ty_count. + METHOD build_menu. + CREATE OBJECT ro_menu. + ro_menu->add( iv_txt = 'Split/Unified view' + iv_act = c_actions-toggle_unified ) ##NO_TEXT. + ENDMETHOD. " build_menu. - CREATE OBJECT lo_html. +********************************************************************** +* EVENT HANDLING +********************************************************************** - ls_stats = is_diff-o_diff->stats( ). + METHOD lif_gui_page~on_event. - IF is_diff-mod = c_mod-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. + CASE iv_action. + WHEN c_actions-toggle_unified. " Toggle file diplay + mv_unified = lcl_app=>user( )->toggle_diff_unified( ). + ev_state = gc_event_state-re_render. + ENDCASE. - lo_html->add( '
' ). "#EC NOTEXT - lo_html->add( |+ { ls_stats-insert }| ). - lo_html->add( |- { ls_stats-delete }| ). - lo_html->add( |~ { ls_stats-update }| ). - lo_html->add( |{ is_diff-filename }| ). "#EC NOTEXT - lo_html->add( lcl_gui_chunk_lib=>render_item_state( iv1 = is_diff-lstate - iv2 = is_diff-rstate ) ). - lo_html->add( '
' ). "#EC NOTEXT + ENDMETHOD. "lif_gui_page~on_event - ro_html = lo_html. - ENDMETHOD. +********************************************************************** +* RENDER LOGIC +********************************************************************** - METHOD render_table_head. + METHOD render_content. + + DATA ls_diff_file LIKE LINE OF mt_diff_files. CREATE OBJECT ro_html. - ro_html->add( '' ). "#EC NOTEXT - ro_html->add( '' ). "#EC NOTEXT - ro_html->add( '' ). "#EC NOTEXT - ro_html->add( 'LOCAL' ). "#EC NOTEXT - ro_html->add( '' ). "#EC NOTEXT - ro_html->add( 'REMOTE' ). "#EC NOTEXT - ro_html->add( '' ). "#EC NOTEXT - ro_html->add( '' ). "#EC NOTEXT + LOOP AT mt_diff_files INTO ls_diff_file. + lcl_progress=>show( iv_key = 'Diff' + iv_current = sy-tabix + iv_total = lines( mt_diff_files ) + iv_text = |Render Diff - { ls_diff_file-filename }| ). - ENDMETHOD. " render_table_head. + ro_html->add( render_diff( ls_diff_file ) ). + ENDLOOP. + + ENDMETHOD. "render_content METHOD render_diff. @@ -232,11 +247,70 @@ CLASS lcl_gui_page_diff IMPLEMENTATION. ro_html->add( '' ). "#EC NOTEXT + ENDMETHOD. " render_diff + +********************************************************************** +* CHUNKS +********************************************************************** + + METHOD render_diff_head. + + DATA: ls_stats TYPE lcl_diff=>ty_count. + + CREATE OBJECT ro_html. + 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( '
' ). "#EC NOTEXT + + ro_html->add( |+ { ls_stats-insert }| ). + ro_html->add( |- { ls_stats-delete }| ). + ro_html->add( |~ { ls_stats-update }| ). + ro_html->add( |{ is_diff-filename }| ). "#EC NOTEXT + ro_html->add( lcl_gui_chunk_lib=>render_item_state( iv1 = is_diff-lstate + iv2 = 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( '
' ). "#EC NOTEXT + ENDMETHOD. + METHOD render_table_head. + + CREATE OBJECT ro_html. + + IF mv_unified = abap_true. + ro_html->add( '' ). "#EC NOTEXT + ro_html->add( '' ). "#EC NOTEXT + ro_html->add( 'old' ). "#EC NOTEXT + ro_html->add( 'new' ). "#EC NOTEXT + ro_html->add( 'code' ). "#EC NOTEXT + ro_html->add( '' ). "#EC NOTEXT + ro_html->add( '' ). "#EC NOTEXT + ELSE. + ro_html->add( '' ). "#EC NOTEXT + ro_html->add( '' ). "#EC NOTEXT + ro_html->add( '' ). "#EC NOTEXT + ro_html->add( 'LOCAL' ). "#EC NOTEXT + ro_html->add( '' ). "#EC NOTEXT + ro_html->add( 'REMOTE' ). "#EC NOTEXT + ro_html->add( '' ). "#EC NOTEXT + ro_html->add( '' ). "#EC NOTEXT + ENDIF. + + ENDMETHOD. " render_table_head. + METHOD render_beacon. - DATA: lv_beacon TYPE string. + DATA: lv_beacon TYPE string. CREATE OBJECT ro_html. @@ -246,10 +320,19 @@ CLASS lcl_gui_page_diff IMPLEMENTATION. lv_beacon = '---'. ENDIF. + ro_html->add( '' ). ro_html->add( '' ). - ro_html->add( '' ). - ro_html->add( |@@ { is_diff_line-new_line } @@ { lv_beacon }| ). + + 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( '' ). + ro_html->add( |@@ { is_diff_line-new_num } @@ { lv_beacon }| ). + ENDIF. + ro_html->add( '' ). ro_html->add( '' ). @@ -259,10 +342,6 @@ CLASS lcl_gui_page_diff IMPLEMENTATION. DATA: lo_highlighter TYPE REF TO lcl_syntax_highlighter, lt_diffs TYPE lcl_diff=>ty_diffs_tt, - lv_local TYPE string, - lv_remote TYPE string, - lv_lattr TYPE string, - lv_rattr TYPE string, lv_insert_nav TYPE abap_bool. FIELD-SYMBOLS LIKE LINE OF lt_diffs. @@ -283,83 +362,125 @@ CLASS lcl_gui_page_diff IMPLEMENTATION. lv_insert_nav = abap_false. ENDIF. - IF is_diff-mod = c_mod-remote. " Remote file leading changes - lv_local = -old. - lv_remote = -new. - ELSE. " Local leading changes or both were modified - lv_local = -new. - lv_remote = -old. - ENDIF. - IF lo_highlighter IS BOUND. - lv_local = lo_highlighter->process_line( lv_local ). - lv_remote = lo_highlighter->process_line( lv_remote ). + -new = lo_highlighter->process_line( -new ). + -old = lo_highlighter->process_line( -old ). ELSE. - lv_local = escape( val = lv_local format = cl_abap_format=>e_html_attr ). - lv_remote = escape( val = lv_remote format = cl_abap_format=>e_html_attr ). + -new = escape( val = -new format = cl_abap_format=>e_html_attr ). + -old = escape( val = -old format = cl_abap_format=>e_html_attr ). ENDIF. - get_line_hl( EXPORTING iv_mod = is_diff-mod - iv_result = -result - IMPORTING ev_lattr = lv_lattr - ev_rattr = lv_rattr ). + CONDENSE -new_num. "get rid of leading spaces + CONDENSE -old_num. - ro_html->add( '' ). "#EC NOTEXT - ro_html->add( |{ -new_line }| ). "#EC NOTEXT - ro_html->add( |{ lv_local }| ). "#EC NOTEXT - ro_html->add( |{ -old_line }| ). "#EC NOTEXT - ro_html->add( |{ lv_remote }| ). "#EC NOTEXT - ro_html->add( '' ). "#EC NOTEXT + 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_fstate = is_diff-fstate ) ). + ENDIF. ENDLOOP. - ENDMETHOD. - - METHOD get_line_hl. - - CLEAR: ev_lattr, ev_rattr. " Class for changed lines - - IF iv_result IS INITIAL. - RETURN. + IF mv_unified = abap_true. + ro_html->add( render_line_unified( ) ). " Release delayed lines ENDIF. - " Both file changed ? Or line updated ? - All yellow - IF iv_mod = c_mod-both OR iv_result = lcl_diff=>c_diff-update. - ev_lattr = ' class="diff_upd"'. "#EC NOTEXT - ev_rattr = ' class="diff_upd"'. "#EC NOTEXT - ELSEIF iv_mod = c_mod-local. " Changed locally - CASE iv_result. - WHEN lcl_diff=>c_diff-insert. - ev_lattr = ' class="diff_ins"'. "#EC NOTEXT - WHEN lcl_diff=>c_diff-delete. - ev_rattr = ' class="diff_del"'. "#EC NOTEXT - ENDCASE. - ELSEIF iv_mod = c_mod-remote. " Changed remotely - invert sides - CASE iv_result. - WHEN lcl_diff=>c_diff-insert. - ev_rattr = ' class="diff_ins"'. "#EC NOTEXT - WHEN lcl_diff=>c_diff-delete. - ev_lattr = ' class="diff_del"'. "#EC NOTEXT - ENDCASE. - ENDIF. + ENDMETHOD. "render_lines - ENDMETHOD. " get_line_hl. + METHOD render_line_split. - METHOD render_content. - - DATA ls_diff_file LIKE LINE OF mt_diff_files. + DATA: lv_new TYPE string, + lv_old TYPE string, + lv_mark TYPE string, + lv_bg TYPE string. CREATE OBJECT ro_html. - LOOP AT mt_diff_files INTO ls_diff_file. - lcl_progress=>show( iv_key = 'Diff' - iv_current = sy-tabix - iv_total = lines( mt_diff_files ) - iv_text = |Render Diff - { ls_diff_file-filename }| ). + " New line + lv_mark = ` `. + IF iv_fstate = c_fstate-both OR is_diff_line-result = lcl_diff=>c_diff-update. + lv_bg = ' diff_upd'. + lv_mark = `~`. + ELSEIF is_diff_line-result = lcl_diff=>c_diff-insert. + lv_bg = ' diff_ins'. + lv_mark = `+`. + ENDIF. + lv_new = || + && |{ lv_mark }{ is_diff_line-new }|. - ro_html->add( render_diff( ls_diff_file ) ). - ENDLOOP. + " Old line + CLEAR lv_bg. + lv_mark = ` `. + IF iv_fstate = c_fstate-both OR is_diff_line-result = lcl_diff=>c_diff-update. + lv_bg = ' diff_upd'. + lv_mark = `~`. + ELSEIF is_diff_line-result = lcl_diff=>c_diff-delete. + lv_bg = ' diff_del'. + lv_mark = `-`. + ENDIF. + lv_old = || + && |{ lv_mark }{ is_diff_line-old }|. - ENDMETHOD. "render_content + " render line, inverse sides if remote is newer + ro_html->add( '' ). "#EC NOTEXT + 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. "render_line_split + + METHOD render_line_unified. + + DATA lv_line TYPE string. + + FIELD-SYMBOLS LIKE LINE OF mt_delayed_lines. + + CREATE OBJECT ro_html. + + " Release delayed subsequent update lines + IF is_diff_line-result <> lcl_diff=>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 lcl_diff=>c_diff-update. + APPEND is_diff_line TO mt_delayed_lines. " Delay output of subsequent updates + WHEN lcl_diff=>c_diff-insert. + ro_html->add( || + && || + && |+{ is_diff_line-new }| ). + WHEN lcl_diff=>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. "render_line_unified ENDCLASS. "lcl_gui_page_diff \ No newline at end of file diff --git a/src/zabapgit_persistence.prog.abap b/src/zabapgit_persistence.prog.abap index d5498f8c0..af4d6a437 100644 --- a/src/zabapgit_persistence.prog.abap +++ b/src/zabapgit_persistence.prog.abap @@ -422,6 +422,14 @@ CLASS lcl_persistence_user DEFINITION FINAL CREATE PRIVATE FRIENDS lcl_app. RETURNING VALUE(rv_changes_only) TYPE abap_bool RAISING lcx_exception. + METHODS toggle_diff_unified + RETURNING VALUE(rv_diff_unified) TYPE abap_bool + RAISING lcx_exception. + + METHODS get_diff_unified + RETURNING VALUE(rv_diff_unified) TYPE abap_bool + RAISING lcx_exception. + METHODS get_favorites RETURNING VALUE(rt_favorites) TYPE tt_favorites RAISING lcx_exception. @@ -454,6 +462,7 @@ CLASS lcl_persistence_user DEFINITION FINAL CREATE PRIVATE FRIENDS lcl_app. repo_config TYPE ty_repo_config_tt, hide_files TYPE abap_bool, changes_only TYPE abap_bool, + diff_unified TYPE abap_bool, favorites TYPE tt_favorites, END OF ty_user. @@ -698,6 +707,24 @@ CLASS lcl_persistence_user IMPLEMENTATION. ENDMETHOD. "get_changes_only + METHOD toggle_diff_unified. + + DATA ls_user TYPE ty_user. + + ls_user = read( ). + ls_user-diff_unified = boolc( ls_user-diff_unified = abap_false ). + update( ls_user ). + + rv_diff_unified = ls_user-diff_unified. + + ENDMETHOD. "toggle_diff_unified + + METHOD get_diff_unified. + + rv_diff_unified = read( )-diff_unified. + + ENDMETHOD. "get_diff_unified + METHOD get_favorites. rt_favorites = read( )-favorites. diff --git a/src/zabapgit_unit_test.prog.abap b/src/zabapgit_unit_test.prog.abap index 8fb06bc0c..22631561e 100644 --- a/src/zabapgit_unit_test.prog.abap +++ b/src/zabapgit_unit_test.prog.abap @@ -242,11 +242,11 @@ CLASS ltcl_diff IMPLEMENTATION. DEFINE _expected. CLEAR ms_expected. - ms_expected-new_line = &1. - ms_expected-new = &2. - ms_expected-result = &3. - ms_expected-old_line = &4. - ms_expected-old = &5. + ms_expected-new_num = &1. + ms_expected-new = &2. + ms_expected-result = &3. + ms_expected-old_num = &4. + ms_expected-old = &5. APPEND ms_expected TO mt_expected. END-OF-DEFINITION. diff --git a/src/zabapgit_util.prog.abap b/src/zabapgit_util.prog.abap index 20478551d..9c9ca66dd 100644 --- a/src/zabapgit_util.prog.abap +++ b/src/zabapgit_util.prog.abap @@ -547,13 +547,13 @@ CLASS lcl_diff DEFINITION FINAL. END OF c_diff. TYPES: BEGIN OF ty_diff, - new_line TYPE c LENGTH 6, - new TYPE string, - result TYPE c LENGTH 1, - old_line TYPE c LENGTH 6, - old TYPE string, - short TYPE abap_bool, - beacon TYPE i, + new_num TYPE c LENGTH 6, + new TYPE string, + result TYPE c LENGTH 1, + old_num TYPE c LENGTH 6, + old TYPE string, + short TYPE abap_bool, + beacon TYPE i, END OF ty_diff. TYPES: ty_diffs_tt TYPE STANDARD TABLE OF ty_diff WITH DEFAULT KEY. @@ -697,16 +697,16 @@ CLASS lcl_diff IMPLEMENTATION. LOOP AT mt_diff ASSIGNING . - -new_line = lv_new. - -old_line = lv_old. + -new_num = lv_new. + -old_num = lv_old. CASE -result. " Line nums WHEN c_diff-delete. lv_old = lv_old + 1. - CLEAR -new_line. + CLEAR -new_num. WHEN c_diff-insert. lv_new = lv_new + 1. - CLEAR -old_line. + CLEAR -old_num. WHEN OTHERS. lv_new = lv_new + 1. lv_old = lv_old + 1.