diff --git a/src/syntax/zcl_abapgit_syntax_highlighter.clas.abap b/src/syntax/zcl_abapgit_syntax_highlighter.clas.abap index 50d55d973..878eca65c 100644 --- a/src/syntax/zcl_abapgit_syntax_highlighter.clas.abap +++ b/src/syntax/zcl_abapgit_syntax_highlighter.clas.abap @@ -30,9 +30,10 @@ CLASS zcl_abapgit_syntax_highlighter DEFINITION TYPES: BEGIN OF ty_rule, - regex TYPE REF TO cl_abap_regex, - token TYPE char1, - style TYPE string, + regex TYPE REF TO cl_abap_regex, + token TYPE char1, + style TYPE string, + relevant_submatch TYPE i, END OF ty_rule. CONSTANTS c_token_none TYPE c VALUE '.'. @@ -41,9 +42,10 @@ CLASS zcl_abapgit_syntax_highlighter DEFINITION METHODS add_rule IMPORTING - iv_regex TYPE string - iv_token TYPE c - iv_style TYPE string. + iv_regex TYPE string + iv_token TYPE c + iv_style TYPE string + iv_submatch TYPE i OPTIONAL. METHODS parse_line IMPORTING iv_line TYPE string @@ -66,12 +68,11 @@ CLASS zcl_abapgit_syntax_highlighter DEFINITION IMPORTING iv_line TYPE string iv_class TYPE string RETURNING VALUE(rv_line) TYPE string. - ENDCLASS. -CLASS ZCL_ABAPGIT_SYNTAX_HIGHLIGHTER IMPLEMENTATION. +CLASS zcl_abapgit_syntax_highlighter IMPLEMENTATION. METHOD add_rule. @@ -83,8 +84,9 @@ CLASS ZCL_ABAPGIT_SYNTAX_HIGHLIGHTER IMPLEMENTATION. pattern = iv_regex ignore_case = abap_true. - ls_rule-token = iv_token. - ls_rule-style = iv_style. + ls_rule-token = iv_token. + ls_rule-style = iv_style. + ls_rule-relevant_submatch = iv_submatch. APPEND ls_rule TO mt_rules. ENDMETHOD. @@ -188,7 +190,8 @@ CLASS ZCL_ABAPGIT_SYNTAX_HIGHLIGHTER IMPLEMENTATION. FIELD-SYMBOLS: LIKE LINE OF mt_rules, - TYPE match_result. + TYPE match_result, + LIKE LINE OF -submatches. CLEAR et_matches. @@ -202,10 +205,21 @@ CLASS ZCL_ABAPGIT_SYNTAX_HIGHLIGHTER IMPLEMENTATION. " Save matches into custom table with predefined tokens LOOP AT lt_result ASSIGNING . CLEAR: ls_match. - ls_match-token = -token. - ls_match-offset = -offset. - ls_match-length = -length. - APPEND ls_match TO et_matches. + IF -relevant_submatch = 0. + ls_match-token = -token. + ls_match-offset = -offset. + ls_match-length = -length. + APPEND ls_match TO et_matches. + ELSE. + READ TABLE -submatches ASSIGNING INDEX -relevant_submatch. + "submatch might be empty if only discarted parts matched + IF sy-subrc = 0 and -offset >= 0 and -length > 0. + ls_match-token = -token. + ls_match-offset = -offset. + ls_match-length = -length. + APPEND ls_match TO et_matches. + ENDIF. + ENDIF. ENDLOOP. ENDLOOP. diff --git a/src/syntax/zcl_abapgit_syntax_highlighter.clas.testclasses.abap b/src/syntax/zcl_abapgit_syntax_highlighter.clas.testclasses.abap index 11a3b9745..45c5b17d7 100644 --- a/src/syntax/zcl_abapgit_syntax_highlighter.clas.testclasses.abap +++ b/src/syntax/zcl_abapgit_syntax_highlighter.clas.testclasses.abap @@ -30,7 +30,10 @@ CLASS ltcl_syntax_cases DEFINITION FINAL FOR TESTING RISK LEVEL HARMLESS test_xml_02 FOR TESTING, test_xml_03 FOR TESTING, test_xml_04 FOR TESTING, - test_xml_05 FOR TESTING. + test_xml_05 FOR TESTING, + test_xml_06 FOR TESTING, + test_xml_07 FOR TESTING, + test_xml_08 FOR TESTING. ENDCLASS. *----------------------------------------------------------------------* @@ -515,6 +518,87 @@ CLASS ltcl_syntax_cases IMPLEMENTATION. ENDMETHOD. + METHOD test_xml_06. + DATA lv_line TYPE string. + + "unclosed tag + lv_line = ' "/>'. "#EC NOTEXT + + " Generate table with expected values after parsing + _generate_parse 'X' 0 1. + _generate_parse 'A' 4 10. + _generate_parse 'V' 15 7. + _generate_parse 'X' 23 1. + + " Generate table with expected values after ordering + _generate_order 'X' 0 4 '<'. + _generate_order 'A' 4 10 ''. + _generate_order 'V' 15 7 ''. + _generate_order 'X' 22 2 '>'. + + " Generate table with expected values after extending + _generate_extend 'X' 0 4 '<'. + _generate_extend 'A' 4 10 ''. + _generate_extend '.' 14 1 ''. + _generate_extend 'V' 15 7 ''. + _generate_extend 'X' 22 2 '>'. + + do_test( iv_line = lv_line iv_filename = '*.xml' ). + + + ENDMETHOD. + + + METHOD test_xml_08. + "invalid XML characters in a string + DATA lv_line TYPE string. + + "attribute at beginning of line + lv_line = 'attribute=''>" '''. "#EC NOTEXT + + " Generate table with expected values after parsing + _generate_parse 'A' 0 9. + _generate_parse 'V' 10 5. + + " Generate table with expected values after ordering + _generate_order 'A' 0 9 ''. + _generate_order 'V' 10 5 ''. + + " Generate table with expected values after extending + _generate_extend 'A' 0 9 ''. + _generate_extend '.' 9 1 ''. + _generate_extend 'V' 10 5 ''. + + do_test( iv_line = lv_line iv_filename = '*.xml' ). + + ENDMETHOD. + ENDCLASS. CLASS ltcl_syntax_basic_logic DEFINITION DEFERRED. diff --git a/src/syntax/zcl_abapgit_syntax_xml.clas.abap b/src/syntax/zcl_abapgit_syntax_xml.clas.abap index 02c83caee..ea73d099c 100644 --- a/src/syntax/zcl_abapgit_syntax_xml.clas.abap +++ b/src/syntax/zcl_abapgit_syntax_xml.clas.abap @@ -19,9 +19,11 @@ CLASS zcl_abapgit_syntax_xml DEFINITION END OF c_token . CONSTANTS: BEGIN OF c_regex, - xml_tag TYPE string VALUE '[<>]', "#EC NOTEXT - attr TYPE string VALUE '\s[-a-z:_0-9]+\s*(?==)', "#EC NOTEXT - attr_val TYPE string VALUE '["''][^''"]*[''"]', "#EC NOTEXT + "for XML tags, we will use a submatch + " main pattern includes quoted strings so we can ignore < and > in attr values + xml_tag TYPE string VALUE '(?:"[^"]*")|(?:''[^'']*'')|([<>])', "#EC NOTEXT + attr TYPE string VALUE '(?:^|\s)[-a-z:_0-9]+\s*(?==)', "#EC NOTEXT + attr_val TYPE string VALUE '("[^"]*")|(''[^'']*'')', "#EC NOTEXT END OF c_regex . METHODS constructor . @@ -42,9 +44,10 @@ CLASS zcl_abapgit_syntax_xml IMPLEMENTATION. " Initialize instances of regular expressions - add_rule( iv_regex = c_regex-xml_tag - iv_token = c_token-xml_tag - iv_style = c_css-xml_tag ). + add_rule( iv_regex = c_regex-xml_tag + iv_token = c_token-xml_tag + iv_style = c_css-xml_tag + iv_submatch = 1 ). add_rule( iv_regex = c_regex-attr iv_token = c_token-attr @@ -114,5 +117,18 @@ CLASS zcl_abapgit_syntax_xml IMPLEMENTATION. ASSIGN TO . ENDLOOP. + "if the last XML tag is not closed, extend it to the end of the tag + IF lv_prev_token = c_token-xml_tag + AND IS ASSIGNED + AND -length = 1 + AND -text_tag = '<'. + + FIND REGEX '<\s*[^\s]*' IN iv_line+-offset MATCH LENGTH -length. + IF sy-subrc <> 0. + -length = 1. + ENDIF. + + ENDIF. + ENDMETHOD. ENDCLASS. diff --git a/src/syntax/zcl_abapgit_syntax_xml.clas.testclasses.abap b/src/syntax/zcl_abapgit_syntax_xml.clas.testclasses.abap index 1255fe685..5cf827f7c 100644 --- a/src/syntax/zcl_abapgit_syntax_xml.clas.testclasses.abap +++ b/src/syntax/zcl_abapgit_syntax_xml.clas.testclasses.abap @@ -11,7 +11,9 @@ CLASS abapgit_syntax_xml DEFINITION FINAL FOR TESTING sole_closing_xml_tag FOR TESTING RAISING cx_static_check, complete_xml_tag FOR TESTING RAISING cx_static_check, complete_xml_tag_with_closing FOR TESTING RAISING cx_static_check, - empty_attributes FOR TESTING RAISING cx_static_check. + empty_attributes FOR TESTING RAISING cx_static_check, + open_tags FOR TESTING RAISING cx_static_check, + attributes_only FOR TESTING RAISING cx_static_check. ENDCLASS. @@ -63,4 +65,39 @@ CLASS abapgit_syntax_xml IMPLEMENTATION. ENDMETHOD. + METHOD attributes_only. + + cl_abap_unit_assert=>assert_equals( + exp = | SAPRL=| + && |"751"| + && | VERSION=| + && |">1.5"| + act = mo_cut->process_line( | SAPRL="751" VERSION=">1.5"| ) ). + + cl_abap_unit_assert=>assert_equals( + exp = |SAPRL=| + && |"751"| + && | VERSION=| + && |'>1.5'| + act = mo_cut->process_line( |SAPRL="751" VERSION='>1.5'| ) ). + + ENDMETHOD. + + METHOD open_tags. + + cl_abap_unit_assert=>assert_equals( + exp = |<ECTD| + act = mo_cut->process_line( |assert_equals( + exp = |<ECTD| + && | SAPRL=| + && |"751"| + && | VERSION=| + && |"1.5"| + act = mo_cut->process_line( |