Staging smaller chunks of code like git add -p (#1842)

* Staging smaller chunks of code like git add -p

With this commit a new feature which works similar to git add -p
is added. We are now able to patch, which means that we can
commit on line basis. And are not restricted anymore to commit
only on file basis.

* move get_page_patch to stage page

* Refactor JS

* Refactor JS
This commit is contained in:
Christian Günter 2018-09-08 08:58:25 +02:00 committed by Lars Hvam
parent 93c518a142
commit 0fa50309a1
13 changed files with 1526 additions and 70 deletions

View File

@ -0,0 +1,118 @@
CLASS zcl_abapgit_git_add_patch DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
METHODS:
constructor
IMPORTING
it_diff TYPE zif_abapgit_definitions=>ty_diffs_tt,
get_patch
RETURNING
VALUE(rt_patch) TYPE stringtab
RAISING
zcx_abapgit_exception,
get_patch_binary
RETURNING
VALUE(rv_patch_binary) TYPE xstring
RAISING
zcx_abapgit_exception.
PRIVATE SECTION.
DATA:
mt_diff TYPE zif_abapgit_definitions=>ty_diffs_tt,
mt_patch TYPE stringtab.
METHODS:
calculate_patch
RETURNING
VALUE(rt_patch) TYPE stringtab
RAISING
zcx_abapgit_exception.
ENDCLASS.
CLASS zcl_abapgit_git_add_patch IMPLEMENTATION.
METHOD calculate_patch.
FIELD-SYMBOLS: <ls_diff> TYPE zif_abapgit_definitions=>ty_diff.
LOOP AT mt_diff ASSIGNING <ls_diff>.
CASE <ls_diff>-result.
WHEN ' '.
INSERT <ls_diff>-new INTO TABLE rt_patch.
WHEN zif_abapgit_definitions=>c_diff-insert.
IF <ls_diff>-patch_flag = abap_true.
INSERT <ls_diff>-new INTO TABLE rt_patch.
ENDIF.
WHEN zif_abapgit_definitions=>c_diff-delete.
IF <ls_diff>-patch_flag = abap_false.
INSERT <ls_diff>-old INTO TABLE rt_patch.
ENDIF.
WHEN zif_abapgit_definitions=>c_diff-update.
IF <ls_diff>-patch_flag = abap_true.
INSERT <ls_diff>-new INTO TABLE rt_patch.
ELSE.
INSERT <ls_diff>-old INTO TABLE rt_patch.
ENDIF.
WHEN OTHERS.
zcx_abapgit_exception=>raise( |Unknown result| ).
ENDCASE.
ENDLOOP.
ENDMETHOD.
METHOD constructor.
mt_diff = it_diff.
ENDMETHOD.
METHOD get_patch.
IF mt_patch IS INITIAL.
mt_patch = calculate_patch( ).
ENDIF.
rt_patch = mt_patch.
ENDMETHOD.
METHOD get_patch_binary.
DATA: lv_string TYPE string.
IF mt_patch IS INITIAL.
mt_patch = calculate_patch( ).
ENDIF.
CONCATENATE LINES OF mt_patch INTO lv_string SEPARATED BY zif_abapgit_definitions=>c_newline.
lv_string = lv_string && zif_abapgit_definitions=>c_newline.
rv_patch_binary = zcl_abapgit_convert=>string_to_xstring_utf8( lv_string ).
ENDMETHOD.
ENDCLASS.

View File

@ -0,0 +1,397 @@
*"* use this source file for your ABAP unit test classes
CLASS ltcl_calculate_patch DEFINITION FINAL FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS:
single_insert FOR TESTING RAISING cx_static_check,
multiple_adjacent_insert FOR TESTING RAISING cx_static_check,
multiple_non_adjacent_insert FOR TESTING RAISING cx_static_check,
multiple_partial_insert FOR TESTING RAISING cx_static_check,
single_delete FOR TESTING RAISING cx_static_check,
multiple_adjacend_delete FOR TESTING RAISING cx_static_check,
multiple_non_adjacent_delete FOR TESTING RAISING cx_static_check,
multiple_partial_delete FOR TESTING RAISING cx_static_check,
single_update FOR TESTING RAISING cx_static_check,
multiple_adjacend_update FOR TESTING RAISING cx_static_check,
multiple_non_adjacent_update FOR TESTING RAISING cx_static_check,
multiple_partial_update FOR TESTING RAISING cx_static_check,
mixed FOR TESTING RAISING cx_static_check,
unknown_result_type FOR TESTING RAISING cx_static_check.
METHODS:
setup,
given_diff
IMPORTING
iv_patch_flag TYPE zif_abapgit_definitions=>ty_diff-patch_flag
iv_new_num TYPE zif_abapgit_definitions=>ty_diff-new_num
iv_new TYPE zif_abapgit_definitions=>ty_diff-new
iv_result TYPE zif_abapgit_definitions=>ty_diff-result
iv_old_num TYPE zif_abapgit_definitions=>ty_diff-old_num
iv_old TYPE zif_abapgit_definitions=>ty_diff-old
iv_short TYPE zif_abapgit_definitions=>ty_diff-short DEFAULT 'X'
iv_beacon TYPE zif_abapgit_definitions=>ty_diff-beacon DEFAULT 1,
when_patch_is_calculated,
then_patch_should_be
IMPORTING
iv_exp_patch TYPE string,
then_exception_is_raised.
DATA:
mt_diff TYPE zif_abapgit_definitions=>ty_diffs_tt,
mt_patch TYPE stringtab,
mv_index TYPE sytabix,
mx_error TYPE REF TO zcx_abapgit_exception.
ENDCLASS.
CLASS ltcl_calculate_patch IMPLEMENTATION.
METHOD setup.
mv_index = 0.
ENDMETHOD.
DEFINE given_diff.
given_diff( iv_patch_flag = &1
iv_new_num = &2
iv_new = &3
iv_result = &4
iv_old_num = &5
iv_old = &6 ).
END-OF-DEFINITION.
METHOD single_insert.
given_diff:
" patch_flag new_num new result old_num old
' ' ' 1' ' ' ' ' ' 1' ' ',
'X' ' 2' 'write: `Test`.' 'I' ' ' ' ',
' ' ' 3' ' ' ' ' ' 2' ' '.
when_patch_is_calculated( ).
then_patch_should_be(:
' ' ),
'write: `Test`.' ),
' ' ).
ENDMETHOD.
METHOD multiple_adjacent_insert.
given_diff:
" patch_flag new_num new result old_num old
' ' ' 1' ' ' ' ' ' 1' ' ',
'X' ' 2' 'write: `Test`.' 'I' ' ' ' ',
'X' ' 3' 'write: `Hello world`.' 'I' ' ' ' ',
' ' ' 4' ' ' ' ' ' 2' ' '.
when_patch_is_calculated( ).
then_patch_should_be(:
' ' ),
'write: `Test`.' ),
'write: `Hello world`.' ),
' ' ).
ENDMETHOD.
METHOD multiple_non_adjacent_insert.
given_diff:
" patch_flag new_num new result old_num old
' ' ' 1' ' ' ' ' ' 1' ' ',
'X' ' 2' 'write: `Test`.' 'I' ' ' ' ',
' ' ' 3' ' ' ' ' ' 2' ' ',
'X' ' 4' 'write: `Hello world`.' 'I' ' ' ' ',
' ' ' 5' ' ' ' ' ' 3' ' '.
when_patch_is_calculated( ).
then_patch_should_be(:
' ' ),
'write: `Test`.' ),
' ' ),
'write: `Hello world`.' ),
' ' ).
ENDMETHOD.
METHOD multiple_partial_insert.
given_diff:
" patch_flag new_num new result old_num old
' ' ' 1' ' ' ' ' ' 1' ' ',
'X' ' 2' 'write: `Test`.' 'I' ' ' ' ',
' ' ' 3' ' ' ' ' ' 2' ' ',
' ' ' 4' 'write: `Hello world`.' 'I' ' ' ' ',
' ' ' 5' ' ' ' ' ' 3' ' '.
when_patch_is_calculated( ).
then_patch_should_be(:
' ' ),
'write: `Test`.' ),
' ' ),
' ' ).
ENDMETHOD.
METHOD single_delete.
given_diff:
" patch_flag new_num new result old_num old
' ' ' 1' ' ' ' ' ' 1' ' ' ,
'X' ' ' ' ' 'D' ' 2' 'write: `Test`.',
' ' ' 2' ' ' ' ' ' 3' ' ' .
when_patch_is_calculated( ).
then_patch_should_be(:
' ' ),
' ' ).
ENDMETHOD.
METHOD multiple_adjacend_delete.
given_diff:
" patch_flag new_num new result old_num old
' ' ' 1' ' ' ' ' ' 1' ' ' ,
'X' ' ' ' ' 'D' ' 2' 'write: `Test`.' ,
'X' ' ' ' ' 'D' ' 3' 'write: `Hello world`.',
' ' ' 2' ' ' ' ' ' 4' ' ' .
when_patch_is_calculated( ).
then_patch_should_be(:
' ' ),
' ' ).
ENDMETHOD.
METHOD multiple_non_adjacent_delete.
given_diff:
" patch_flag new_num new result old_num old
' ' ' 1' ' ' ' ' ' 1' ' ' ,
'X' ' ' ' ' 'D' ' 2' 'write: `Test`.' ,
' ' ' ' ' ' 'D' ' 3' 'write: `Hello world`.',
'X' ' ' ' ' 'D' ' 4' 'write: `Hello 123`.' ,
' ' ' 2' ' ' ' ' ' 5' ' ' .
when_patch_is_calculated( ).
then_patch_should_be(:
' ' ),
'write: `Hello world`.' ),
' ' ).
ENDMETHOD.
METHOD multiple_partial_delete.
given_diff:
" patch_flag new_num new result old_num old
' ' ' 1' ' ' ' ' ' 1' ' ' ,
'X' ' ' ' ' 'D' ' 2' 'write: `Test`.' ,
' ' ' ' ' ' 'D' ' 3' 'write: `Hello world`.',
' ' ' ' ' ' 'D' ' 4' 'write: `Hello 123`.' ,
'X' ' ' ' ' 'D' ' 5' 'write: `Hello test`.' ,
' ' ' 2' ' ' ' ' ' 6' ' ' .
when_patch_is_calculated( ).
then_patch_should_be( ' ' ).
then_patch_should_be( 'write: `Hello world`.' ).
then_patch_should_be( 'write: `Hello 123`.' ).
then_patch_should_be( ' ' ).
ENDMETHOD.
METHOD single_update.
given_diff:
" patch_flag new_num new result old_num old
' ' ' 1' ' ' ' ' ' 1' ' ' ,
'X' ' 2' 'write: `Hello world`.' 'U' ' 2' 'write: `Test`.',
' ' ' 3' ' ' ' ' ' 3' ' ' .
when_patch_is_calculated( ).
then_patch_should_be(:
' ' ),
'write: `Hello world`.' ),
' ' ).
ENDMETHOD.
METHOD multiple_adjacend_update.
given_diff:
" patch_flag new_num new result old_num old
' ' ' 1' ' ' ' ' ' 1' ' ' ,
'X' ' 2' 'write: `Hello world`.' 'U' ' 2' 'write: `Test`.' ,
'X' ' 3' 'write: `Test`.' 'U' ' 3' 'write: `Hello world`.',
' ' ' 4' ' ' ' ' ' 4' ' ' .
when_patch_is_calculated( ).
then_patch_should_be(:
' ' ),
'write: `Hello world`.' ),
'write: `Test`.' ),
' ' ).
ENDMETHOD.
METHOD multiple_non_adjacent_update.
given_diff:
" patch_flag new_num new result old_num old
' ' ' 1' ' ' ' ' ' 1' ' ' ,
'X' ' 2' 'write: `Hello world`.' 'U' ' 2' 'write: `Test`.' ,
' ' ' 3' ' ' ' ' ' 3' ' ' ,
'X' ' 4' 'write: `Test`.' 'U' ' 4' 'write: `Hello world`.',
' ' ' 5' ' ' ' ' ' 5' ' ' .
when_patch_is_calculated( ).
then_patch_should_be(:
' ' ),
'write: `Hello world`.' ),
' ' ),
'write: `Test`.' ),
' ' ).
ENDMETHOD.
METHOD multiple_partial_update.
given_diff:
" patch_flag new_num new result old_num old
' ' ' 1' ' ' ' ' ' 1' ' ' ,
'X' ' 2' 'write: `Hello world`.' 'U' ' 2' 'write: `Test`.' ,
' ' ' 3' ' ' ' ' ' 3' ' ' ,
' ' ' 4' 'write: `Test`.' 'U' ' 4' 'write: `Hello world`.',
' ' ' 5' ' ' ' ' ' 5' ' ' .
when_patch_is_calculated( ).
then_patch_should_be(:
' ' ),
'write: `Hello world`.' ),
' ' ),
'write: `Hello world`.' ),
' ' ).
ENDMETHOD.
METHOD mixed.
given_diff:
" patch_flag new_num new result old_num old
' ' ' 1' ' ' ' ' ' 1' ' ' ,
'X' ' 2' 'write: `Hello world`.' 'U' ' 2' 'write: `Test`.' ,
' ' ' 3' ' ' ' ' ' 3' ' ' ,
' ' ' 4' 'write: `Test`.' 'U' ' 4' 'write: `Hello world`.',
' ' ' 5' ' ' ' ' ' 5' ' ' ,
'X' ' 6' 'write: `newline`.' 'I' ' ' ' ' .
when_patch_is_calculated( ).
then_patch_should_be(:
' ' ),
'write: `Hello world`.' ),
' ' ),
'write: `Hello world`.' ),
' ' ),
'write: `newline`.' ).
ENDMETHOD.
METHOD unknown_result_type.
given_diff:
" patch_flag new_num new result old_num old
' ' ' 1' ' ' 'X' ' 1' ' '.
when_patch_is_calculated( ).
then_exception_is_raised( ).
ENDMETHOD.
METHOD given_diff.
DATA: ls_diff LIKE LINE OF mt_diff.
ls_diff-patch_flag = iv_patch_flag.
ls_diff-new_num = iv_new_num.
ls_diff-new = iv_new.
ls_diff-result = iv_result.
ls_diff-old_num = iv_old_num.
ls_diff-old = iv_old.
ls_diff-short = iv_short.
ls_diff-beacon = iv_beacon.
INSERT ls_diff INTO TABLE mt_diff.
ENDMETHOD.
METHOD when_patch_is_calculated.
DATA: lo_git_add_patch TYPE REF TO zcl_abapgit_git_add_patch.
CREATE OBJECT lo_git_add_patch
EXPORTING
it_diff = mt_diff.
TRY.
mt_patch = lo_git_add_patch->get_patch( ).
CATCH zcx_abapgit_exception INTO mx_error.
ENDTRY.
ENDMETHOD.
METHOD then_patch_should_be.
FIELD-SYMBOLS: <ls_patch> LIKE LINE OF mt_patch.
mv_index = mv_index + 1.
READ TABLE mt_patch INDEX mv_index
ASSIGNING <ls_patch>.
cl_abap_unit_assert=>assert_equals(
exp = iv_exp_patch
act = <ls_patch> ).
ENDMETHOD.
METHOD then_exception_is_raised.
cl_abap_unit_assert=>assert_equals(
exp = |Unknown result|
act = mx_error->get_text( ) ).
ENDMETHOD.
ENDCLASS.

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
<asx:values>
<VSEOCLASS>
<CLSNAME>ZCL_ABAPGIT_GIT_ADD_PATCH</CLSNAME>
<VERSION>1</VERSION>
<LANGU>E</LANGU>
<DESCRIPT>git add -p</DESCRIPT>
<EXPOSURE>2</EXPOSURE>
<STATE>1</STATE>
<CLSFINAL>X</CLSFINAL>
<CLSCCINCL>X</CLSCCINCL>
<FIXPT>X</FIXPT>
<UNICODE>X</UNICODE>
<WITH_UNIT_TESTS>X</WITH_UNIT_TESTS>
</VSEOCLASS>
</asx:values>
</asx:abap>
</abapGit>

View File

@ -1,52 +1,70 @@
CLASS zcl_abapgit_gui_page_diff DEFINITION
PUBLIC
INHERITING FROM zcl_abapgit_gui_page
FINAL
CREATE PUBLIC INHERITING FROM zcl_abapgit_gui_page.
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES: zif_abapgit_gui_page_hotkey.
INTERFACES zif_abapgit_gui_page_hotkey .
TYPES:
BEGIN OF ty_file_diff,
path TYPE string,
filename 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.
END OF c_fstate .
TYPES: BEGIN OF ty_file_diff,
path TYPE string,
filename 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,
tt_file_diff TYPE STANDARD TABLE OF ty_file_diff.
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
!io_stage TYPE REF TO zcl_abapgit_stage OPTIONAL
!iv_patch_mode TYPE abap_bool DEFAULT abap_false
RAISING
zcx_abapgit_exception .
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
RAISING zcx_abapgit_exception,
zif_abapgit_gui_page~on_event REDEFINITION.
METHODS zif_abapgit_gui_page~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.
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_seed TYPE string, " Unique page id to bind JS sessionStorage
mv_patch_mode TYPE abap_bool,
mo_stage TYPE REF TO zcl_abapgit_stage.
METHODS render_diff
IMPORTING is_diff TYPE ty_file_diff
@ -55,6 +73,7 @@ CLASS zcl_abapgit_gui_page_diff DEFINITION
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
@ -65,7 +84,9 @@ CLASS zcl_abapgit_gui_page_diff DEFINITION
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 sytabix
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
@ -81,6 +102,63 @@ CLASS zcl_abapgit_gui_page_diff DEFINITION
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 sytabix.
METHODS start_staging
IMPORTING
it_postdata TYPE cnht_post_data_tab
RAISING
zcx_abapgit_exception .
METHODS apply_patch_all
IMPORTING
iv_action TYPE ty_patch_action
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 zcl_abapgit_gui_page_diff=>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.
CLASS-METHODS get_patch_data
IMPORTING
iv_patch TYPE string
iv_action TYPE string
EXPORTING
ev_filename TYPE string
ev_line_index TYPE string
RAISING
zcx_abapgit_exception.
ENDCLASS.
@ -88,6 +166,50 @@ ENDCLASS.
CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
METHOD add_to_stage.
DATA: lo_repo TYPE REF TO zcl_abapgit_repo_online,
lt_local TYPE zif_abapgit_definitions=>ty_files_item_tt,
lt_diff TYPE zif_abapgit_definitions=>ty_diffs_tt,
lv_something_patched TYPE abap_bool,
lv_patch TYPE xstring,
lo_git_add_patch TYPE REF TO zcl_abapgit_git_add_patch.
FIELD-SYMBOLS: <ls_diff_file> TYPE zcl_abapgit_gui_page_diff=>ty_file_diff.
lo_repo ?= zcl_abapgit_repo_srv=>get_instance( )->get( mv_repo_key ).
lt_local = lo_repo->get_files_local( ).
LOOP AT mt_diff_files ASSIGNING <ls_diff_file>.
lt_diff = <ls_diff_file>-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 = <ls_diff_file>-o_diff->get( ).
lv_patch = lo_git_add_patch->get_patch_binary( ).
mo_stage->add(
iv_path = <ls_diff_file>-path
iv_filename = <ls_diff_file>-filename
iv_data = lv_patch ).
ENDLOOP.
IF lv_something_patched = abap_false.
zcx_abapgit_exception=>raise( |Nothing added| ).
ENDIF.
ENDMETHOD.
METHOD append_diff.
DATA:
@ -173,6 +295,67 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
ENDMETHOD.
METHOD apply_patch_all.
DATA: lv_filename TYPE string,
lt_patch TYPE string_table,
lv_line_index TYPE string.
FIELD-SYMBOLS: <lv_patch> TYPE LINE OF string_table.
SPLIT iv_patch AT ',' INTO TABLE lt_patch.
LOOP AT lt_patch ASSIGNING <lv_patch>.
get_patch_data(
EXPORTING
iv_patch = <lv_patch>
iv_action = iv_action
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 build_menu.
DATA: lo_sub TYPE REF TO zcl_abapgit_html_toolbar,
@ -222,6 +405,14 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
io_sub = lo_sub ) ##NO_TEXT.
ENDIF.
IF mv_patch_mode = abap_true.
ro_menu->add( iv_txt = 'Stage'
iv_act = c_actions-stage
iv_id = 'stage'
iv_typ = zif_abapgit_definitions=>c_action_type-dummy
) ##NO_TEXT.
ENDIF.
ro_menu->add( iv_txt = 'Split/Unified view'
iv_act = c_actions-toggle_unified ) ##NO_TEXT.
@ -242,6 +433,13 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
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.
mo_stage = io_stage.
IF mv_patch_mode = abap_true.
" While patching we always want to be in split mode
CLEAR: mv_unified.
ENDIF.
GET TIME STAMP FIELD lv_ts.
mv_seed = |diff{ lv_ts }|. " Generate based on time
@ -292,6 +490,25 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
ENDMETHOD.
METHOD get_patch_data.
CLEAR: ev_filename, ev_line_index.
IF iv_action <> c_patch_action-add
AND iv_action <> c_patch_action-remove.
zcx_abapgit_exception=>raise( |Invalid action { iv_action }| ).
ENDIF.
FIND FIRST OCCURRENCE OF REGEX iv_action && `_patch_(.*)_(\d+)`
IN iv_patch
SUBMATCHES ev_filename ev_line_index.
IF sy-subrc <> 0.
zcx_abapgit_exception=>raise( |Invalid patch| ).
ENDIF.
ENDMETHOD.
METHOD is_binary.
DATA: lv_len TYPE i,
@ -399,8 +616,8 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
" Content
IF is_diff-type <> 'binary'.
ro_html->add( '<div class="diff_content">' ). "#EC NOTEXT
ro_html->add( '<table class="diff_tab syntax-hl">' ). "#EC NOTEXT
ro_html->add( render_table_head( ) ).
ro_html->add( |<table class="diff_tab syntax-hl" id={ is_diff-filename }>| ). "#EC NOTEXT
ro_html->add( render_table_head( is_diff ) ).
ro_html->add( render_lines( is_diff ) ).
ro_html->add( '</table>' ). "#EC NOTEXT
ELSE.
@ -457,7 +674,8 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
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_insert_nav TYPE abap_bool,
lv_tabix TYPE syst-tabix.
FIELD-SYMBOLS <ls_diff> LIKE LINE OF lt_diffs.
@ -467,6 +685,9 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
lt_diffs = is_diff-o_diff->get( ).
LOOP AT lt_diffs ASSIGNING <ls_diff>.
lv_tabix = sy-tabix.
IF <ls_diff>-short = abap_false.
lv_insert_nav = abap_true.
CONTINUE.
@ -492,7 +713,9 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
ro_html->add( render_line_unified( is_diff_line = <ls_diff> ) ).
ELSE.
ro_html->add( render_line_split( is_diff_line = <ls_diff>
iv_fstate = is_diff-fstate ) ).
iv_filename = is_diff-filename
iv_fstate = is_diff-fstate
iv_index = lv_tabix ) ).
ENDIF.
ENDLOOP.
@ -506,10 +729,11 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
METHOD render_line_split.
DATA: lv_new TYPE string,
lv_old TYPE string,
lv_mark TYPE string,
lv_bg TYPE string.
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.
@ -525,6 +749,10 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
lv_new = |<td class="num" line-num="{ is_diff_line-new_num }"></td>|
&& |<td class="code{ lv_bg }">{ lv_mark }{ is_diff_line-new }</td>|.
IF lv_mark <> ` `.
lv_patch_line_possible = abap_true.
ENDIF.
" Old line
CLEAR lv_bg.
lv_mark = ` `.
@ -538,8 +766,23 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
lv_old = |<td class="num" line-num="{ is_diff_line-old_num }"></td>|
&& |<td class="code{ lv_bg }">{ lv_mark }{ is_diff_line-old }</td>|.
IF lv_mark <> ` `.
lv_patch_line_possible = abap_true.
ENDIF.
" render line, inverse sides if remote is newer
ro_html->add( '<tr>' ). "#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
@ -547,6 +790,7 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
ro_html->add( lv_new ). " local
ro_html->add( lv_old ). " remote
ENDIF.
ro_html->add( '</tr>' ). "#EC NOTEXT
ENDMETHOD.
@ -599,6 +843,82 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
ENDMETHOD.
METHOD render_patch.
CONSTANTS:
BEGIN OF c_css_class,
patch_active TYPE string VALUE `patch-active` ##NO_TEXT,
patch TYPE string VALUE `patch` ##NO_TEXT,
END OF c_css_class.
DATA: lv_id TYPE string,
lv_left_class TYPE string,
lv_right_class TYPE string,
lv_object TYPE string.
lv_object = iv_filename.
IF iv_patch_line_possible = abap_true.
lv_id = |patch_{ lv_object }_{ iv_index }|.
io_html->add( |<td class="{ c_css_class-patch }">| ).
lv_left_class = |{ c_patch_action-add } |.
lv_right_class = |{ c_patch_action-remove } |.
IF is_diff_line-patch_flag = abap_true.
lv_left_class = lv_left_class && |{ c_css_class-patch_active }|.
ELSE.
lv_right_class = lv_right_class && |{ c_css_class-patch_active }|.
ENDIF.
io_html->add_a( iv_txt = |{ c_patch_action-add }|
iv_act = ||
iv_id = |{ c_patch_action-add }_{ lv_id }|
iv_typ = zif_abapgit_definitions=>c_action_type-dummy
iv_class = lv_left_class ).
io_html->add_a( iv_txt = |{ c_patch_action-remove }|
iv_act = ||
iv_id = |{ c_patch_action-remove }_{ lv_id }|
iv_typ = zif_abapgit_definitions=>c_action_type-dummy
iv_class = lv_right_class ).
io_html->add( |</td>| ).
ELSE.
io_html->add( |<td class="{ c_css_class-patch }">| ).
io_html->add( |</td>| ).
ENDIF.
ENDMETHOD.
METHOD render_patch_head.
io_html->add( |<th class="patch">| ).
io_html->add_a( iv_txt = |{ c_patch_action-add }|
iv_act = |patch_add_all('{ is_diff-filename }')|
iv_id = |patch_add_all|
iv_typ = zif_abapgit_definitions=>c_action_type-dummy ).
io_html->add_a( iv_txt = |{ c_patch_action-remove }|
iv_act = |patch_remove_all('{ is_diff-filename }')|
iv_id = |patch_remove_all|
iv_typ = zif_abapgit_definitions=>c_action_type-dummy ).
io_html->add( '</th>' ).
ENDMETHOD.
METHOD render_table_head.
CREATE OBJECT ro_html.
@ -611,10 +931,19 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
ro_html->add( '<th class="num">new</th>' ). "#EC NOTEXT
ro_html->add( '<th>code</th>' ). "#EC NOTEXT
ELSE.
IF mv_patch_mode = abap_true.
render_patch_head( io_html = ro_html
is_diff = is_diff ).
ENDIF.
ro_html->add( '<th class="num"></th>' ). "#EC NOTEXT
ro_html->add( '<th>LOCAL</th>' ). "#EC NOTEXT
ro_html->add( '<th class="num"></th>' ). "#EC NOTEXT
ro_html->add( '<th>REMOTE</th>' ). "#EC NOTEXT
ENDIF.
ro_html->add( '</tr>' ). "#EC NOTEXT
@ -635,22 +964,113 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
ro_html->add( ' }' ).
ro_html->add( '});' ).
IF mv_patch_mode = abap_true.
ro_html->add( 'preparePatch();' ).
ro_html->add( 'registerStagePatch();' ).
ENDIF.
ENDMETHOD.
METHOD zif_abapgit_gui_page~on_event.
METHOD start_staging.
CASE iv_action.
WHEN c_actions-toggle_unified. " Toggle file diplay
mv_unified = zcl_abapgit_persistence_user=>get_instance( )->toggle_diff_unified( ).
ev_state = zif_abapgit_definitions=>c_event_state-re_render.
ENDCASE.
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_action = c_patch_action-add
iv_patch = lv_add
iv_patch_flag = abap_true ).
apply_patch_all( iv_action = c_patch_action-remove
iv_patch = lv_remove
iv_patch_flag = abap_false ).
add_to_stage( ).
ENDMETHOD.
METHOD zif_abapgit_gui_page_hotkey~get_hotkey_actions.
DATA: ls_hotkey_action LIKE LINE OF rt_hotkey_actions.
ls_hotkey_action-name = |Diff: Stage|.
ls_hotkey_action-action = |stagePatch|.
ls_hotkey_action-default_hotkey = |s|.
INSERT ls_hotkey_action INTO TABLE rt_hotkey_actions.
ENDMETHOD.
METHOD zif_abapgit_gui_page~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 = zif_abapgit_definitions=>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 = zif_abapgit_definitions=>c_event_state-new_page.
ENDCASE.
ENDMETHOD.
METHOD get_diff_object.
FIELD-SYMBOLS: <ls_diff_file> LIKE LINE OF mt_diff_files.
READ TABLE mt_diff_files ASSIGNING <ls_diff_file>
WITH KEY filename = iv_filename.
IF sy-subrc <> 0.
zcx_abapgit_exception=>raise( |Invalid filename { iv_filename }| ).
ENDIF.
ro_diff = <ls_diff_file>-o_diff.
ENDMETHOD.
METHOD get_diff_line.
DATA: lt_diff TYPE zif_abapgit_definitions=>ty_diffs_tt,
lv_line_index TYPE sytabix.
FIELD-SYMBOLS: <ls_diff> LIKE LINE OF lt_diff.
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.
ENDCLASS.

View File

@ -0,0 +1,142 @@
*"* use this source file for your ABAP unit test classes
CLASS ltcl_patch DEFINITION FINAL FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS:
get_patch_data_add FOR TESTING RAISING cx_static_check,
get_patch_data_remove FOR TESTING RAISING cx_static_check,
invalid_action FOR TESTING RAISING cx_static_check,
invalid_patch_missing_file FOR TESTING RAISING cx_static_check,
invalid_patch_missing_index FOR TESTING RAISING cx_static_check.
ENDCLASS.
CLASS zcl_abapgit_gui_page_diff DEFINITION LOCAL FRIENDS ltcl_patch.
CLASS ltcl_patch IMPLEMENTATION.
METHOD get_patch_data_add.
DATA: lv_file_name TYPE string,
lv_line_index TYPE string.
zcl_abapgit_gui_page_diff=>get_patch_data(
EXPORTING
iv_patch = |add_patch_zcl_test_git_add_p.clas.abap_19|
iv_action = |add|
IMPORTING
ev_filename = lv_file_name
ev_line_index = lv_line_index ).
cl_abap_unit_assert=>assert_equals(
exp = |zcl_test_git_add_p.clas.abap|
act = lv_file_name ).
cl_abap_unit_assert=>assert_equals(
exp = |19|
act = lv_line_index ).
ENDMETHOD.
METHOD get_patch_data_remove.
DATA: lv_file_name TYPE string,
lv_line_index TYPE string.
zcl_abapgit_gui_page_diff=>get_patch_data(
EXPORTING
iv_patch = |remove_patch_ztest_patch.prog.abap_39|
iv_action = |remove|
IMPORTING
ev_filename = lv_file_name
ev_line_index = lv_line_index ).
cl_abap_unit_assert=>assert_equals(
exp = |ztest_patch.prog.abap|
act = lv_file_name ).
cl_abap_unit_assert=>assert_equals(
exp = |39|
act = lv_line_index ).
ENDMETHOD.
METHOD invalid_action.
DATA: lv_file_name TYPE string,
lv_line_index TYPE string,
lx_error TYPE REF TO zcx_abapgit_exception.
TRY.
zcl_abapgit_gui_page_diff=>get_patch_data(
EXPORTING
iv_patch = |remove_patch_ztest_patch.prog.abap_39|
iv_action = |mix|
IMPORTING
ev_filename = lv_file_name
ev_line_index = lv_line_index ).
cl_abap_unit_assert=>fail( ).
CATCH zcx_abapgit_exception INTO lx_error.
cl_abap_unit_assert=>assert_equals(
exp = |Invalid action mix|
act = lx_error->get_text( ) ).
ENDTRY.
ENDMETHOD.
METHOD invalid_patch_missing_file.
DATA: lv_file_name TYPE string,
lv_line_index TYPE string,
lx_error TYPE REF TO zcx_abapgit_exception.
TRY.
zcl_abapgit_gui_page_diff=>get_patch_data(
EXPORTING
iv_patch = |add_patch_39|
iv_action = |add|
IMPORTING
ev_filename = lv_file_name
ev_line_index = lv_line_index ).
cl_abap_unit_assert=>fail( ).
CATCH zcx_abapgit_exception INTO lx_error.
cl_abap_unit_assert=>assert_equals(
exp = |Invalid patch|
act = lx_error->get_text( ) ).
ENDTRY.
ENDMETHOD.
METHOD invalid_patch_missing_index.
DATA: lv_file_name TYPE string,
lv_line_index TYPE string,
lx_error TYPE REF TO zcx_abapgit_exception.
TRY.
zcl_abapgit_gui_page_diff=>get_patch_data(
EXPORTING
iv_patch = |remove_patch_ztest_patch.prog.abap|
iv_action = |remove|
IMPORTING
ev_filename = lv_file_name
ev_line_index = lv_line_index ).
cl_abap_unit_assert=>fail( ).
CATCH zcx_abapgit_exception INTO lx_error.
cl_abap_unit_assert=>assert_equals(
exp = |Invalid patch|
act = lx_error->get_text( ) ).
ENDTRY.
ENDMETHOD.
ENDCLASS.

View File

@ -13,6 +13,7 @@
<CLSCCINCL>X</CLSCCINCL>
<FIXPT>X</FIXPT>
<UNICODE>X</UNICODE>
<WITH_UNIT_TESTS>X</WITH_UNIT_TESTS>
</VSEOCLASS>
</asx:values>
</asx:abap>

View File

@ -66,11 +66,16 @@ CLASS zcl_abapgit_gui_page_stage DEFINITION
METHODS build_menu
RETURNING
VALUE(ro_menu) TYPE REF TO zcl_abapgit_html_toolbar .
METHODS get_page_patch
IMPORTING iv_getdata TYPE clike
iv_prev_page TYPE clike
RETURNING VALUE(ri_page) TYPE REF TO zif_abapgit_gui_page
RAISING zcx_abapgit_exception.
ENDCLASS.
CLASS ZCL_ABAPGIT_GUI_PAGE_STAGE IMPLEMENTATION.
CLASS zcl_abapgit_gui_page_stage IMPLEMENTATION.
METHOD build_menu.
@ -182,7 +187,9 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_STAGE IMPLEMENTATION.
METHOD render_actions.
DATA: lv_local_count TYPE i,
lv_add_all_txt TYPE string.
lv_add_all_txt TYPE string,
lv_param TYPE string,
ls_file TYPE zif_abapgit_definitions=>ty_file.
CREATE OBJECT ro_html.
lv_local_count = lines( ms_files-local ).
@ -204,6 +211,17 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_STAGE IMPLEMENTATION.
ro_html->add_a( iv_act = |{ c_action-stage_all }|
iv_id = 'commitAllButton'
iv_txt = lv_add_all_txt ) ##NO_TEXT.
lv_param = zcl_abapgit_html_action_utils=>file_encode( iv_key = mo_repo->get_key( )
ig_file = ls_file ).
ro_html->add( '</td>' ).
ro_html->add( '<td class="pad-sides">' ).
ro_html->add_a(
iv_txt = |Patch|
iv_act = |{ zif_abapgit_definitions=>c_action-go_patch }?{ lv_param }| ).
ro_html->add( '</td>' ).
" Filter bar
@ -212,7 +230,8 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_STAGE IMPLEMENTATION.
' type="search" placeholder="Filter objects">' ).
ro_html->add( '</td>' ).
ro_html->add( '</tr></table>' ).
ro_html->add( '</tr>' ).
ro_html->add( '</table>' ).
ENDMETHOD. "render_actions
@ -268,6 +287,7 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_STAGE IMPLEMENTATION.
ro_html->add( |<td class="status">?</td>| ).
ro_html->add( '<td class="cmd"></td>' ). " Command added in JS
ro_html->add( '</tr>' ).
ENDMETHOD. "render_file
@ -367,6 +387,13 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_STAGE IMPLEMENTATION.
METHOD zif_abapgit_gui_page_hotkey~get_hotkey_actions.
DATA: ls_hotkey_action TYPE zif_abapgit_gui_page_hotkey=>ty_hotkey_action.
ls_hotkey_action-name = |Stage: Patch|.
ls_hotkey_action-action = zif_abapgit_definitions=>c_action-go_patch.
ls_hotkey_action-default_hotkey = |p|.
INSERT ls_hotkey_action INTO TABLE rt_hotkey_actions.
ENDMETHOD.
@ -383,23 +410,72 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_STAGE IMPLEMENTATION.
CASE iv_action.
WHEN c_action-stage_all.
LOOP AT ms_files-local ASSIGNING <ls_file>.
lo_stage->add( iv_path = <ls_file>-file-path
iv_filename = <ls_file>-file-filename
iv_data = <ls_file>-file-data ).
ENDLOOP.
CREATE OBJECT ei_page TYPE zcl_abapgit_gui_page_commit
EXPORTING
io_repo = mo_repo
io_stage = lo_stage.
ev_state = zif_abapgit_definitions=>c_event_state-new_page.
ev_state = zif_abapgit_definitions=>c_event_state-new_page.
WHEN c_action-stage_commit.
process_stage_list( it_postdata = it_postdata io_stage = lo_stage ).
CREATE OBJECT ei_page TYPE zcl_abapgit_gui_page_commit
EXPORTING
io_repo = mo_repo
io_stage = lo_stage.
ev_state = zif_abapgit_definitions=>c_event_state-new_page.
WHEN zif_abapgit_definitions=>c_action-go_patch. " Go Patch page
ei_page = get_page_patch(
iv_getdata = iv_getdata
iv_prev_page = iv_prev_page ).
ev_state = zif_abapgit_definitions=>c_event_state-new_page.
WHEN OTHERS.
RETURN.
ENDCASE.
CREATE OBJECT ei_page TYPE zcl_abapgit_gui_page_commit
EXPORTING
io_repo = mo_repo
io_stage = lo_stage.
ENDMETHOD.
ev_state = zif_abapgit_definitions=>c_event_state-new_page.
METHOD get_page_patch.
DATA: lo_page TYPE REF TO zcl_abapgit_gui_page_diff,
lv_key TYPE zif_abapgit_persistence=>ty_repo-key,
ls_file TYPE zif_abapgit_definitions=>ty_file,
ls_object TYPE zif_abapgit_definitions=>ty_item,
lo_stage TYPE REF TO zcl_abapgit_stage.
zcl_abapgit_html_action_utils=>file_obj_decode(
EXPORTING
iv_string = iv_getdata
IMPORTING
ev_key = lv_key
eg_file = ls_file
eg_object = ls_object ).
CREATE OBJECT lo_stage.
CREATE OBJECT lo_page
EXPORTING
iv_key = lv_key
iv_patch_mode = abap_true
io_stage = lo_stage.
ri_page = lo_page.
ENDMETHOD.
ENDCLASS.

View File

@ -44,7 +44,7 @@ ENDCLASS.
CLASS ZCL_ABAPGIT_GUI_ROUTER IMPLEMENTATION.
CLASS zcl_abapgit_gui_router IMPLEMENTATION.
METHOD get_page_background.

View File

@ -16,6 +16,21 @@ CLASS zcl_abapgit_diff DEFINITION
METHODS stats
RETURNING 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.
METHODS set_patch_old
IMPORTING
iv_line_old TYPE i
iv_patch_flag TYPE abap_bool
RAISING
zcx_abapgit_exception.
PRIVATE SECTION.
DATA mt_diff TYPE zif_abapgit_definitions=>ty_diffs_tt.
DATA ms_stats TYPE zif_abapgit_definitions=>ty_count.
@ -258,6 +273,54 @@ CLASS ZCL_ABAPGIT_DIFF IMPLEMENTATION.
ENDMETHOD.
METHOD set_patch_new.
DATA: lv_new_num TYPE i.
FIELD-SYMBOLS: <ls_diff> TYPE zif_abapgit_definitions=>ty_diff.
LOOP AT mt_diff ASSIGNING <ls_diff>.
lv_new_num = <ls_diff>-new_num.
IF lv_new_num = iv_line_new.
EXIT.
ENDIF.
ENDLOOP.
IF sy-subrc <> 0.
zcx_abapgit_exception=>raise( |Invalid new line number { iv_line_new }| ).
ENDIF.
<ls_diff>-patch_flag = iv_patch_flag.
ENDMETHOD.
METHOD set_patch_old.
DATA: lv_old_num TYPE i.
FIELD-SYMBOLS: <ls_diff> TYPE zif_abapgit_definitions=>ty_diff.
LOOP AT mt_diff ASSIGNING <ls_diff>.
lv_old_num = <ls_diff>-old_num.
IF lv_old_num = iv_line_old.
EXIT.
ENDIF.
ENDLOOP.
IF sy-subrc <> 0.
zcx_abapgit_exception=>raise( |Invalid old line number { iv_line_old }| ).
ENDIF.
<ls_diff>-patch_flag = iv_patch_flag.
ENDMETHOD.
METHOD shortlist.
DATA: lv_index TYPE i.

View File

@ -503,6 +503,18 @@ table.diff_tab td.num, th.num {
-ms-user-select: none;
user-select: none;
}
table.diff_tab td.patch, th.patch {
width: 1%;
min-width: 6em;
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);
}
@ -882,3 +894,8 @@ div.news .update {
border-style: solid;
border-color: #555 transparent transparent transparent;
}
/* diff-patch */
.patch-active {
color: lightgrey !important;
}

View File

@ -289,7 +289,7 @@ StageHelper.prototype.onTableClick = function (event) {
}
this.updateMenu();
}
};
// Search object
StageHelper.prototype.onSearch = function (e) {
@ -682,7 +682,7 @@ function enableArrowListNavigation() {
document.addEventListener('keydown', oKeyNavigation.onkeydown.bind(oKeyNavigation));
};
}
function LinkHints(sLinkHintKey, sColor){
this.sLinkHintKey = sLinkHintKey;
@ -723,13 +723,12 @@ LinkHints.prototype.fnRenderTooltips = function () {
// we stick to numeric values and just increment them.
var
iTooltipCounter = this.getTooltipStartValue(this.aTooltipElements.length),
that = this;
iTooltipCounter = this.getTooltipStartValue(this.aTooltipElements.length);
[].forEach.call(this.aTooltipElements, function(oTooltip){
iTooltipCounter += 1;
this.fnRenderTooltip(oTooltip, iTooltipCounter)
}.bind(that));
}.bind(this));
};
@ -754,8 +753,6 @@ LinkHints.prototype.fnRemoveAllTooltips = function () {
LinkHints.prototype.fnFilterTooltips = function (sPending) {
var that = this;
Object
.keys(this.oTooltipMap)
.forEach(function (sKey) {
@ -772,7 +769,7 @@ LinkHints.prototype.fnFilterTooltips = function (sPending) {
oTooltip.classList.add('hidden');
}
}.bind(that));
}.bind(this));
};
@ -844,26 +841,30 @@ function setLinkHints(sLinkHintKey, sColor) {
function Hotkeys(oKeyMap){
var that = this;
this.oKeyMap = oKeyMap || {};
// these are the hotkeys provided by the backend
Object.keys(this.oKeyMap).forEach(function(sKey){
var action = that.oKeyMap[sKey];
var action = this.oKeyMap[sKey];
// We replace the actions with callback functions to unify
// the hotkey execution
that.oKeyMap[sKey] = function(oEvent) {
this.oKeyMap[sKey] = function(oEvent) {
// We have either a js function
if (that[action]) {
that[action].call(that);
// We have either a js function on this
if (this[action]) {
this[action].call(this);
return;
}
// Or a global function
if (window[action]) {
window[action].call(this);
}
// Or a SAP event
var sUiSapEvent = that.getSapEvent(action);
var sUiSapEvent = this.getSapEvent(action);
if (sUiSapEvent) {
submitSapeventForm({}, sUiSapEvent, "post");
oEvent.preventDefault();
@ -872,7 +873,7 @@ function Hotkeys(oKeyMap){
}
});
}.bind(this));
}
@ -936,3 +937,191 @@ function setKeyBindings(oKeyMap){
document.addEventListener('keydown', oHotkeys.onkeydown.bind(oHotkeys));
}
/*
Patch / git add -p
*/
function CSSPatchClassCombination(sClassLinkClicked, sClassCorrespondingLink){
this.sClassLinkClicked = sClassLinkClicked;
this.sClassCorrespondingLink = sClassCorrespondingLink;
}
function Patch() {
this.CSS_CLASS = {
ADD: 'add',
REMOVE: 'remove',
PATCH: 'patch',
PATCH_ACTIVE: 'patch-active'
}
this.ID = {
STAGE: 'stage',
PATCH_ADD_ALL: 'patch_add_all',
PATCH_REMOVE_ALL: 'patch_remove_all'
}
this.ACTION = {
PATCH_STAGE: 'patch_stage'
}
this.ADD_REMOVE = new CSSPatchClassCombination(this.CSS_CLASS.ADD, this.CSS_CLASS.REMOVE);
this.REMOVE_ADD = new CSSPatchClassCombination(this.CSS_CLASS.REMOVE, this.CSS_CLASS.ADD);
}
Patch.prototype.preparePatch = function(){
this.registerClickHandlerSingleLine();
this.registerClickHandlerAllFile();
}
Patch.prototype.registerClickHandlerSingleLine = function(){
// registers the link handlers for add and remove single lines
this.registerClickHandlerForPatchLink(this.ADD_REMOVE);
this.registerClickHandlerForPatchLink(this.REMOVE_ADD);
}
Patch.prototype.registerClickHandlerAllFile = function(){
// registers the link handlers for add and remove all changes for a file
this.registerClickHandlerForPatchLinkAll('#' + this.ID.PATCH_ADD_ALL, this.ADD_REMOVE);
this.registerClickHandlerForPatchLinkAll('#' + this.ID.PATCH_REMOVE_ALL, this.REMOVE_ADD);
}
Patch.prototype.registerClickHandlerForPatchLink = function(oClassCombination) {
// register onclick handler. When a link is clicked it is
// deactivated and its corresponding link gets active
//
// e.g. if you click on 'add' add is deactivated and 'remove'
// is activated.
var elLinkAll = document.querySelectorAll('.' + this.CSS_CLASS.PATCH + ' a.' + oClassCombination.sClassLinkClicked);
[].forEach.call(elLinkAll,function(elLink){
elLink.addEventListener('click',function(oEvent){
this.togglePatchActiveForClassLink(oEvent, elLink, oClassCombination);
}.bind(this));
}.bind(this));
}
Patch.prototype.togglePatchActive = function(oEvent, elClicked, elCorrespondingLink){
if (!elClicked.classList.contains(this.CSS_CLASS.PATCH_ACTIVE)){
elClicked.classList.toggle(this.CSS_CLASS.PATCH_ACTIVE);
elCorrespondingLink.classList.toggle(this.CSS_CLASS.PATCH_ACTIVE);
}
oEvent.preventDefault();
}
Patch.prototype.togglePatchActiveForClassLink = function(oEvent, elClicked, oClassCombination) {
var sCorrespondingLinkId = this.getCorrespodingLinkId(elClicked.id, oClassCombination);
var elCorrespondingLink = document.querySelector('#' + this.escapeDots(sCorrespondingLinkId));
this.togglePatchActive(oEvent, elClicked, elCorrespondingLink);
}
Patch.prototype.getCorrespodingLinkId = function(sClickedLinkId, oClassCombination){
// e.g.
//
// add_patch_z_test_git_add_p.prog.abap_28 => remove_patch_z_test_git_add_p.prog.abap_28
//
// and vice versa
var oRegexPatchClassPrefix = new RegExp('^' + oClassCombination.sClassLinkClicked );
return sClickedLinkId.replace(oRegexPatchClassPrefix, oClassCombination.sClassCorrespondingLink);
}
Patch.prototype.escapeDots = function(sFileName){
return sFileName.replace(/\./g,'\\.');
}
Patch.prototype.patchLinkClickAll = function(oClassCombination) {
return function(oEvent) {
var sTableId = oEvent.srcElement.parentElement.parentElement.parentElement.parentElement.id;
var elAddAll = document.querySelectorAll('#' + this.escapeDots(sTableId) + ' a.' + oClassCombination.sClassLinkClicked);
[].forEach.call(elAddAll,function(elem){
this.togglePatchActiveForClassLink(oEvent, elem, oClassCombination);
}.bind(this));
oEvent.preventDefault();
}
}
Patch.prototype.registerClickHandlerForPatchLinkAll = function(sSelector, oClassCombination){
var elAll = document.querySelectorAll(sSelector);
[].forEach.call(elAll, function(elem){
elem.addEventListener('click', this.patchLinkClickAll(oClassCombination).bind(this));
}.bind(this));
}
Patch.prototype.registerStagePatch = function registerStagePatch(){
var elStage = document.querySelector('#' + this.ID.STAGE);
elStage.addEventListener('click', this.stagePatch.bind(this));
// for hotkeys
window.stagePatch = function(){
this.stagePatch();
}.bind(this);
}
Patch.prototype.stagePatch = function() {
// Collect add and remove info and submit to backend
var aAddPatch = this.collectActiveElementsForSelector('.' + this.CSS_CLASS.PATCH +' a.' + this.CSS_CLASS.ADD);
var aRemovePatch = this.collectActiveElementsForSelector('.' + this.CSS_CLASS.PATCH + ' a.' + this.CSS_CLASS.REMOVE);
submitSapeventForm({'add': aAddPatch, 'remove': aRemovePatch}, this.ACTION.PATCH_STAGE, "post");
}
Patch.prototype.collectActiveElementsForSelector = function(sSelector){
return [].slice.call(document.querySelectorAll(sSelector))
.filter(function(elem){
return elem.classList.contains(this.CSS_CLASS.PATCH_ACTIVE)
}.bind(this))
.map(function(elem){
return elem.id;
});
};
function preparePatch(){
var oPatch = new Patch();
oPatch.preparePatch();
}
function registerStagePatch(){
var oPatch = new Patch();
oPatch.registerStagePatch();
}

View File

@ -84,14 +84,16 @@ ENDCLASS.
CLASS ZCL_ABAPGIT_STAGE IMPLEMENTATION.
CLASS zcl_abapgit_stage IMPLEMENTATION.
METHOD add.
append( iv_path = iv_path
iv_filename = iv_filename
iv_method = c_method-add
iv_data = iv_data ).
ENDMETHOD. "add

View File

@ -263,13 +263,14 @@ INTERFACE zif_abapgit_definitions PUBLIC.
END OF c_diff.
TYPES: BEGIN OF ty_diff,
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,
patch_flag TYPE abap_bool,
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.
@ -347,6 +348,15 @@ INTERFACE zif_abapgit_definitions PUBLIC.
TYPES:
tty_dokil TYPE STANDARD TABLE OF dokil
WITH NON-UNIQUE DEFAULT KEY.
TYPES: tty_lines TYPE STANDARD TABLE OF i
WITH NON-UNIQUE DEFAULT KEY,
BEGIN OF ty_patch,
filename TYPE string,
lines_new TYPE tty_lines,
lines_old TYPE tty_lines,
END OF ty_patch,
tty_patch TYPE HASHED TABLE OF ty_patch
WITH UNIQUE KEY filename.
CONSTANTS:
BEGIN OF c_type,
@ -456,6 +466,7 @@ INTERFACE zif_abapgit_definitions PUBLIC.
go_debuginfo TYPE string VALUE 'go_debuginfo',
go_settings TYPE string VALUE 'go_settings',
go_tutorial TYPE string VALUE 'go_tutorial',
go_patch TYPE string VALUE 'go_patch',
jump TYPE string VALUE 'jump',
jump_pkg TYPE string VALUE 'jump_pkg',