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 CLASS zcl_abapgit_gui_page_diff DEFINITION
PUBLIC PUBLIC
INHERITING FROM zcl_abapgit_gui_page
FINAL FINAL
CREATE PUBLIC INHERITING FROM zcl_abapgit_gui_page. CREATE PUBLIC .
PUBLIC SECTION. 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: CONSTANTS:
BEGIN OF c_fstate, BEGIN OF c_fstate,
local TYPE char1 VALUE 'L', local TYPE char1 VALUE 'L',
remote TYPE char1 VALUE 'R', remote TYPE char1 VALUE 'R',
both TYPE char1 VALUE 'B', both TYPE char1 VALUE 'B',
END OF c_fstate. END OF c_fstate .
TYPES: BEGIN OF ty_file_diff, METHODS constructor
path TYPE string, IMPORTING
filename TYPE string, !iv_key TYPE zif_abapgit_persistence=>ty_repo-key
lstate TYPE char1, !is_file TYPE zif_abapgit_definitions=>ty_file OPTIONAL
rstate TYPE char1, !is_object TYPE zif_abapgit_definitions=>ty_item OPTIONAL
fstate TYPE char1, " FILE state - Abstraction for shorter ifs !io_stage TYPE REF TO zcl_abapgit_stage OPTIONAL
o_diff TYPE REF TO zcl_abapgit_diff, !iv_patch_mode TYPE abap_bool DEFAULT abap_false
changed_by TYPE xubname, RAISING
type TYPE string, zcx_abapgit_exception .
END OF ty_file_diff,
tt_file_diff TYPE STANDARD TABLE OF ty_file_diff.
METHODS: METHODS zif_abapgit_gui_page~on_event
constructor REDEFINITION .
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.
PROTECTED SECTION. PROTECTED SECTION.
METHODS: METHODS:
render_content REDEFINITION, render_content REDEFINITION,
scripts REDEFINITION. scripts REDEFINITION.
PRIVATE SECTION. PRIVATE SECTION.
TYPES: ty_patch_action TYPE string.
CONSTANTS: BEGIN OF c_actions, CONSTANTS: BEGIN OF c_actions,
stage TYPE string VALUE 'patch_stage',
toggle_unified TYPE string VALUE 'toggle_unified', 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, DATA: mt_diff_files TYPE tt_file_diff,
mt_delayed_lines TYPE zif_abapgit_definitions=>ty_diffs_tt, mt_delayed_lines TYPE zif_abapgit_definitions=>ty_diffs_tt,
mv_unified TYPE abap_bool VALUE abap_true, mv_unified TYPE abap_bool VALUE abap_true,
mv_repo_key TYPE zif_abapgit_persistence=>ty_repo-key, 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 METHODS render_diff
IMPORTING is_diff TYPE ty_file_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 IMPORTING is_diff TYPE ty_file_diff
RETURNING VALUE(ro_html) TYPE REF TO zcl_abapgit_html. RETURNING VALUE(ro_html) TYPE REF TO zcl_abapgit_html.
METHODS render_table_head METHODS render_table_head
IMPORTING is_diff TYPE ty_file_diff
RETURNING VALUE(ro_html) TYPE REF TO zcl_abapgit_html. RETURNING VALUE(ro_html) TYPE REF TO zcl_abapgit_html.
METHODS render_lines METHODS render_lines
IMPORTING is_diff TYPE ty_file_diff 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. RETURNING VALUE(ro_html) TYPE REF TO zcl_abapgit_html.
METHODS render_line_split METHODS render_line_split
IMPORTING is_diff_line TYPE zif_abapgit_definitions=>ty_diff IMPORTING is_diff_line TYPE zif_abapgit_definitions=>ty_diff
iv_filename TYPE string
iv_fstate TYPE char1 iv_fstate TYPE char1
iv_index TYPE sytabix
RETURNING VALUE(ro_html) TYPE REF TO zcl_abapgit_html. RETURNING VALUE(ro_html) TYPE REF TO zcl_abapgit_html.
METHODS render_line_unified METHODS render_line_unified
IMPORTING is_diff_line TYPE zif_abapgit_definitions=>ty_diff OPTIONAL 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 IMPORTING iv_d1 TYPE xstring
iv_d2 TYPE xstring iv_d2 TYPE xstring
RETURNING VALUE(rv_yes) TYPE abap_bool. 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. ENDCLASS.
@ -88,6 +166,50 @@ ENDCLASS.
CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION. 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. METHOD append_diff.
DATA: DATA:
@ -173,6 +295,67 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
ENDMETHOD. 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. METHOD build_menu.
DATA: lo_sub TYPE REF TO zcl_abapgit_html_toolbar, 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. io_sub = lo_sub ) ##NO_TEXT.
ENDIF. 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' ro_menu->add( iv_txt = 'Split/Unified view'
iv_act = c_actions-toggle_unified ) ##NO_TEXT. iv_act = c_actions-toggle_unified ) ##NO_TEXT.
@ -242,6 +433,13 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
ms_control-page_title = 'DIFF'. ms_control-page_title = 'DIFF'.
mv_unified = zcl_abapgit_persistence_user=>get_instance( )->get_diff_unified( ). mv_unified = zcl_abapgit_persistence_user=>get_instance( )->get_diff_unified( ).
mv_repo_key = iv_key. 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. GET TIME STAMP FIELD lv_ts.
mv_seed = |diff{ lv_ts }|. " Generate based on time mv_seed = |diff{ lv_ts }|. " Generate based on time
@ -292,6 +490,25 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
ENDMETHOD. 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. METHOD is_binary.
DATA: lv_len TYPE i, DATA: lv_len TYPE i,
@ -399,8 +616,8 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
" Content " Content
IF is_diff-type <> 'binary'. IF is_diff-type <> 'binary'.
ro_html->add( '<div class="diff_content">' ). "#EC NOTEXT ro_html->add( '<div class="diff_content">' ). "#EC NOTEXT
ro_html->add( '<table class="diff_tab syntax-hl">' ). "#EC NOTEXT ro_html->add( |<table class="diff_tab syntax-hl" id={ is_diff-filename }>| ). "#EC NOTEXT
ro_html->add( render_table_head( ) ). ro_html->add( render_table_head( is_diff ) ).
ro_html->add( render_lines( is_diff ) ). ro_html->add( render_lines( is_diff ) ).
ro_html->add( '</table>' ). "#EC NOTEXT ro_html->add( '</table>' ). "#EC NOTEXT
ELSE. ELSE.
@ -457,7 +674,8 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
DATA: lo_highlighter TYPE REF TO zcl_abapgit_syntax_highlighter, DATA: lo_highlighter TYPE REF TO zcl_abapgit_syntax_highlighter,
lt_diffs TYPE zif_abapgit_definitions=>ty_diffs_tt, 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. 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( ). lt_diffs = is_diff-o_diff->get( ).
LOOP AT lt_diffs ASSIGNING <ls_diff>. LOOP AT lt_diffs ASSIGNING <ls_diff>.
lv_tabix = sy-tabix.
IF <ls_diff>-short = abap_false. IF <ls_diff>-short = abap_false.
lv_insert_nav = abap_true. lv_insert_nav = abap_true.
CONTINUE. CONTINUE.
@ -492,7 +713,9 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
ro_html->add( render_line_unified( is_diff_line = <ls_diff> ) ). ro_html->add( render_line_unified( is_diff_line = <ls_diff> ) ).
ELSE. ELSE.
ro_html->add( render_line_split( is_diff_line = <ls_diff> 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. ENDIF.
ENDLOOP. ENDLOOP.
@ -506,10 +729,11 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
METHOD render_line_split. METHOD render_line_split.
DATA: lv_new TYPE string, DATA: lv_new TYPE string,
lv_old TYPE string, lv_old TYPE string,
lv_mark TYPE string, lv_mark TYPE string,
lv_bg TYPE string. lv_bg TYPE string,
lv_patch_line_possible TYPE abap_bool.
CREATE OBJECT ro_html. 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>| 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>|. && |<td class="code{ lv_bg }">{ lv_mark }{ is_diff_line-new }</td>|.
IF lv_mark <> ` `.
lv_patch_line_possible = abap_true.
ENDIF.
" Old line " Old line
CLEAR lv_bg. CLEAR lv_bg.
lv_mark = ` `. 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>| 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>|. && |<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 " render line, inverse sides if remote is newer
ro_html->add( '<tr>' ). "#EC NOTEXT 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 IF iv_fstate = c_fstate-remote. " Remote file leading changes
ro_html->add( lv_old ). " local ro_html->add( lv_old ). " local
ro_html->add( lv_new ). " remote 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_new ). " local
ro_html->add( lv_old ). " remote ro_html->add( lv_old ). " remote
ENDIF. ENDIF.
ro_html->add( '</tr>' ). "#EC NOTEXT ro_html->add( '</tr>' ). "#EC NOTEXT
ENDMETHOD. ENDMETHOD.
@ -599,6 +843,82 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
ENDMETHOD. 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. METHOD render_table_head.
CREATE OBJECT ro_html. 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 class="num">new</th>' ). "#EC NOTEXT
ro_html->add( '<th>code</th>' ). "#EC NOTEXT ro_html->add( '<th>code</th>' ). "#EC NOTEXT
ELSE. 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 class="num"></th>' ). "#EC NOTEXT
ro_html->add( '<th>LOCAL</th>' ). "#EC NOTEXT ro_html->add( '<th>LOCAL</th>' ). "#EC NOTEXT
ro_html->add( '<th class="num"></th>' ). "#EC NOTEXT ro_html->add( '<th class="num"></th>' ). "#EC NOTEXT
ro_html->add( '<th>REMOTE</th>' ). "#EC NOTEXT ro_html->add( '<th>REMOTE</th>' ). "#EC NOTEXT
ENDIF. ENDIF.
ro_html->add( '</tr>' ). "#EC NOTEXT ro_html->add( '</tr>' ). "#EC NOTEXT
@ -635,22 +964,113 @@ CLASS zcl_abapgit_gui_page_diff IMPLEMENTATION.
ro_html->add( ' }' ). ro_html->add( ' }' ).
ro_html->add( '});' ). ro_html->add( '});' ).
IF mv_patch_mode = abap_true.
ro_html->add( 'preparePatch();' ).
ro_html->add( 'registerStagePatch();' ).
ENDIF.
ENDMETHOD. ENDMETHOD.
METHOD zif_abapgit_gui_page~on_event. METHOD start_staging.
CASE iv_action. DATA: lv_string TYPE string,
WHEN c_actions-toggle_unified. " Toggle file diplay lt_fields TYPE tihttpnvp,
mv_unified = zcl_abapgit_persistence_user=>get_instance( )->toggle_diff_unified( ). lv_add TYPE string,
ev_state = zif_abapgit_definitions=>c_event_state-re_render. lv_remove TYPE string.
ENDCASE.
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. ENDMETHOD.
METHOD zif_abapgit_gui_page_hotkey~get_hotkey_actions. 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. ENDMETHOD.
ENDCLASS. 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> <CLSCCINCL>X</CLSCCINCL>
<FIXPT>X</FIXPT> <FIXPT>X</FIXPT>
<UNICODE>X</UNICODE> <UNICODE>X</UNICODE>
<WITH_UNIT_TESTS>X</WITH_UNIT_TESTS>
</VSEOCLASS> </VSEOCLASS>
</asx:values> </asx:values>
</asx:abap> </asx:abap>

View File

@ -66,11 +66,16 @@ CLASS zcl_abapgit_gui_page_stage DEFINITION
METHODS build_menu METHODS build_menu
RETURNING RETURNING
VALUE(ro_menu) TYPE REF TO zcl_abapgit_html_toolbar . 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. ENDCLASS.
CLASS ZCL_ABAPGIT_GUI_PAGE_STAGE IMPLEMENTATION. CLASS zcl_abapgit_gui_page_stage IMPLEMENTATION.
METHOD build_menu. METHOD build_menu.
@ -182,7 +187,9 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_STAGE IMPLEMENTATION.
METHOD render_actions. METHOD render_actions.
DATA: lv_local_count TYPE i, 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. CREATE OBJECT ro_html.
lv_local_count = lines( ms_files-local ). 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 }| ro_html->add_a( iv_act = |{ c_action-stage_all }|
iv_id = 'commitAllButton' iv_id = 'commitAllButton'
iv_txt = lv_add_all_txt ) ##NO_TEXT. 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>' ). ro_html->add( '</td>' ).
" Filter bar " Filter bar
@ -212,7 +230,8 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_STAGE IMPLEMENTATION.
' type="search" placeholder="Filter objects">' ). ' type="search" placeholder="Filter objects">' ).
ro_html->add( '</td>' ). ro_html->add( '</td>' ).
ro_html->add( '</tr></table>' ). ro_html->add( '</tr>' ).
ro_html->add( '</table>' ).
ENDMETHOD. "render_actions 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="status">?</td>| ).
ro_html->add( '<td class="cmd"></td>' ). " Command added in JS ro_html->add( '<td class="cmd"></td>' ). " Command added in JS
ro_html->add( '</tr>' ). ro_html->add( '</tr>' ).
ENDMETHOD. "render_file ENDMETHOD. "render_file
@ -367,6 +387,13 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_STAGE IMPLEMENTATION.
METHOD zif_abapgit_gui_page_hotkey~get_hotkey_actions. 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. ENDMETHOD.
@ -383,23 +410,72 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_STAGE IMPLEMENTATION.
CASE iv_action. CASE iv_action.
WHEN c_action-stage_all. WHEN c_action-stage_all.
LOOP AT ms_files-local ASSIGNING <ls_file>. LOOP AT ms_files-local ASSIGNING <ls_file>.
lo_stage->add( iv_path = <ls_file>-file-path lo_stage->add( iv_path = <ls_file>-file-path
iv_filename = <ls_file>-file-filename iv_filename = <ls_file>-file-filename
iv_data = <ls_file>-file-data ). iv_data = <ls_file>-file-data ).
ENDLOOP. 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. WHEN c_action-stage_commit.
process_stage_list( it_postdata = it_postdata io_stage = lo_stage ). 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. WHEN OTHERS.
RETURN. RETURN.
ENDCASE. ENDCASE.
CREATE OBJECT ei_page TYPE zcl_abapgit_gui_page_commit ENDMETHOD.
EXPORTING
io_repo = mo_repo
io_stage = lo_stage.
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. ENDMETHOD.
ENDCLASS. ENDCLASS.

View File

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

View File

@ -16,6 +16,21 @@ CLASS zcl_abapgit_diff DEFINITION
METHODS stats METHODS stats
RETURNING VALUE(rs_count) TYPE zif_abapgit_definitions=>ty_count. 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. PRIVATE SECTION.
DATA mt_diff TYPE zif_abapgit_definitions=>ty_diffs_tt. DATA mt_diff TYPE zif_abapgit_definitions=>ty_diffs_tt.
DATA ms_stats TYPE zif_abapgit_definitions=>ty_count. DATA ms_stats TYPE zif_abapgit_definitions=>ty_count.
@ -258,6 +273,54 @@ CLASS ZCL_ABAPGIT_DIFF IMPLEMENTATION.
ENDMETHOD. 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. METHOD shortlist.
DATA: lv_index TYPE i. DATA: lv_index TYPE i.

View File

@ -503,6 +503,18 @@ table.diff_tab td.num, th.num {
-ms-user-select: none; -ms-user-select: none;
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 { table.diff_tab td.num::before {
content: attr(line-num); content: attr(line-num);
} }
@ -882,3 +894,8 @@ div.news .update {
border-style: solid; border-style: solid;
border-color: #555 transparent transparent transparent; 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(); this.updateMenu();
} };
// Search object // Search object
StageHelper.prototype.onSearch = function (e) { StageHelper.prototype.onSearch = function (e) {
@ -682,7 +682,7 @@ function enableArrowListNavigation() {
document.addEventListener('keydown', oKeyNavigation.onkeydown.bind(oKeyNavigation)); document.addEventListener('keydown', oKeyNavigation.onkeydown.bind(oKeyNavigation));
}; }
function LinkHints(sLinkHintKey, sColor){ function LinkHints(sLinkHintKey, sColor){
this.sLinkHintKey = sLinkHintKey; this.sLinkHintKey = sLinkHintKey;
@ -723,13 +723,12 @@ LinkHints.prototype.fnRenderTooltips = function () {
// we stick to numeric values and just increment them. // we stick to numeric values and just increment them.
var var
iTooltipCounter = this.getTooltipStartValue(this.aTooltipElements.length), iTooltipCounter = this.getTooltipStartValue(this.aTooltipElements.length);
that = this;
[].forEach.call(this.aTooltipElements, function(oTooltip){ [].forEach.call(this.aTooltipElements, function(oTooltip){
iTooltipCounter += 1; iTooltipCounter += 1;
this.fnRenderTooltip(oTooltip, iTooltipCounter) this.fnRenderTooltip(oTooltip, iTooltipCounter)
}.bind(that)); }.bind(this));
}; };
@ -754,8 +753,6 @@ LinkHints.prototype.fnRemoveAllTooltips = function () {
LinkHints.prototype.fnFilterTooltips = function (sPending) { LinkHints.prototype.fnFilterTooltips = function (sPending) {
var that = this;
Object Object
.keys(this.oTooltipMap) .keys(this.oTooltipMap)
.forEach(function (sKey) { .forEach(function (sKey) {
@ -772,7 +769,7 @@ LinkHints.prototype.fnFilterTooltips = function (sPending) {
oTooltip.classList.add('hidden'); oTooltip.classList.add('hidden');
} }
}.bind(that)); }.bind(this));
}; };
@ -844,26 +841,30 @@ function setLinkHints(sLinkHintKey, sColor) {
function Hotkeys(oKeyMap){ function Hotkeys(oKeyMap){
var that = this;
this.oKeyMap = oKeyMap || {}; this.oKeyMap = oKeyMap || {};
// these are the hotkeys provided by the backend // these are the hotkeys provided by the backend
Object.keys(this.oKeyMap).forEach(function(sKey){ 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 // We replace the actions with callback functions to unify
// the hotkey execution // the hotkey execution
that.oKeyMap[sKey] = function(oEvent) { this.oKeyMap[sKey] = function(oEvent) {
// We have either a js function // We have either a js function on this
if (that[action]) { if (this[action]) {
that[action].call(that); this[action].call(this);
return; return;
} }
// Or a global function
if (window[action]) {
window[action].call(this);
}
// Or a SAP event // Or a SAP event
var sUiSapEvent = that.getSapEvent(action); var sUiSapEvent = this.getSapEvent(action);
if (sUiSapEvent) { if (sUiSapEvent) {
submitSapeventForm({}, sUiSapEvent, "post"); submitSapeventForm({}, sUiSapEvent, "post");
oEvent.preventDefault(); 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)); 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. METHOD add.
append( iv_path = iv_path append( iv_path = iv_path
iv_filename = iv_filename iv_filename = iv_filename
iv_method = c_method-add iv_method = c_method-add
iv_data = iv_data ). iv_data = iv_data ).
ENDMETHOD. "add ENDMETHOD. "add

View File

@ -263,13 +263,14 @@ INTERFACE zif_abapgit_definitions PUBLIC.
END OF c_diff. END OF c_diff.
TYPES: BEGIN OF ty_diff, TYPES: BEGIN OF ty_diff,
new_num TYPE c LENGTH 6, patch_flag TYPE abap_bool,
new TYPE string, new_num TYPE c LENGTH 6,
result TYPE c LENGTH 1, new TYPE string,
old_num TYPE c LENGTH 6, result TYPE c LENGTH 1,
old TYPE string, old_num TYPE c LENGTH 6,
short TYPE abap_bool, old TYPE string,
beacon TYPE i, short TYPE abap_bool,
beacon TYPE i,
END OF ty_diff. END OF ty_diff.
TYPES: ty_diffs_tt TYPE STANDARD TABLE OF ty_diff WITH DEFAULT KEY. TYPES: ty_diffs_tt TYPE STANDARD TABLE OF ty_diff WITH DEFAULT KEY.
@ -347,6 +348,15 @@ INTERFACE zif_abapgit_definitions PUBLIC.
TYPES: TYPES:
tty_dokil TYPE STANDARD TABLE OF dokil tty_dokil TYPE STANDARD TABLE OF dokil
WITH NON-UNIQUE DEFAULT KEY. 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: CONSTANTS:
BEGIN OF c_type, BEGIN OF c_type,
@ -456,6 +466,7 @@ INTERFACE zif_abapgit_definitions PUBLIC.
go_debuginfo TYPE string VALUE 'go_debuginfo', go_debuginfo TYPE string VALUE 'go_debuginfo',
go_settings TYPE string VALUE 'go_settings', go_settings TYPE string VALUE 'go_settings',
go_tutorial TYPE string VALUE 'go_tutorial', go_tutorial TYPE string VALUE 'go_tutorial',
go_patch TYPE string VALUE 'go_patch',
jump TYPE string VALUE 'jump', jump TYPE string VALUE 'jump',
jump_pkg TYPE string VALUE 'jump_pkg', jump_pkg TYPE string VALUE 'jump_pkg',