*&---------------------------------------------------------------------* *& Include ZABAPGIT_SYNTAX_HIGHLIGHTER *&---------------------------------------------------------------------* CLASS ltcl_syntax_cases DEFINITION DEFERRED. CLASS ltcl_syntax_basic_logic DEFINITION DEFERRED. CLASS lcl_syntax_abap DEFINITION DEFERRED. CLASS lcl_syntax_xml DEFINITION DEFERRED. *----------------------------------------------------------------------* * CLASS lcl_syntax_highlighter DEFINITION *----------------------------------------------------------------------* CLASS lcl_syntax_highlighter DEFINITION ABSTRACT FRIENDS ltcl_syntax_cases ltcl_syntax_basic_logic. PUBLIC SECTION. CLASS-METHODS create IMPORTING iv_filename TYPE string RETURNING VALUE(ro_instance) TYPE REF TO lcl_syntax_highlighter. METHODS process_line IMPORTING iv_line TYPE string RETURNING VALUE(rv_line) TYPE string. PROTECTED SECTION. TYPES: BEGIN OF ty_match, token TYPE char1, " Type of matches offset TYPE i, " Beginning position of the string that should be formatted length TYPE i, " Length of the string that should be formatted text_tag TYPE string, " Type of text tag END OF ty_match. TYPES: ty_match_tt TYPE STANDARD TABLE OF ty_match WITH DEFAULT KEY. TYPES: BEGIN OF ty_rule, regex TYPE REF TO cl_abap_regex, token TYPE char1, style TYPE string, END OF ty_rule. CONSTANTS c_token_none TYPE c VALUE '.'. DATA mt_rules TYPE STANDARD TABLE OF ty_rule. METHODS add_rule IMPORTING iv_regex TYPE string iv_token TYPE c iv_style TYPE string. METHODS parse_line IMPORTING iv_line TYPE string EXPORTING et_matches TYPE ty_match_tt. METHODS order_matches ABSTRACT IMPORTING iv_line TYPE string CHANGING ct_matches TYPE ty_match_tt. METHODS extend_matches IMPORTING iv_line TYPE string CHANGING ct_matches TYPE ty_match_tt. METHODS format_line IMPORTING iv_line TYPE string it_matches TYPE ty_match_tt RETURNING VALUE(rv_line) TYPE string. METHODS apply_style IMPORTING iv_line TYPE string iv_class TYPE string RETURNING VALUE(rv_line) TYPE string. ENDCLASS. " lcl_syntax_highlighter DEFINITION *----------------------------------------------------------------------* * CLASS lcl_syntax_abap DEFINITION *----------------------------------------------------------------------* CLASS lcl_syntax_abap DEFINITION INHERITING FROM lcl_syntax_highlighter FINAL. PUBLIC SECTION. CLASS-METHODS class_constructor. METHODS constructor. CONSTANTS: BEGIN OF c_css, keyword TYPE string VALUE 'keyword', "#EC NOTEXT text TYPE string VALUE 'text', "#EC NOTEXT comment TYPE string VALUE 'comment', "#EC NOTEXT END OF c_css, BEGIN OF c_token, keyword TYPE c VALUE 'K', "#EC NOTEXT text TYPE c VALUE 'T', "#EC NOTEXT comment TYPE c VALUE 'C', "#EC NOTEXT END OF c_token, BEGIN OF c_regex, comment TYPE string VALUE '##|"|^\*', text TYPE string VALUE '`|''|\||\{|\}', keyword TYPE string VALUE '&&|\b[-_a-z0-9]+\b', END OF c_regex. PROTECTED SECTION. CLASS-DATA gt_keywords TYPE HASHED TABLE OF string WITH UNIQUE KEY table_line. CLASS-METHODS init_keywords. CLASS-METHODS is_keyword IMPORTING iv_chunk TYPE string RETURNING VALUE(rv_yes) TYPE abap_bool. METHODS order_matches REDEFINITION. METHODS parse_line REDEFINITION. ENDCLASS. " lcl_syntax_abap DEFINITION *----------------------------------------------------------------------* * CLASS lcl_syntax_xml DEFINITION *----------------------------------------------------------------------* CLASS lcl_syntax_xml DEFINITION INHERITING FROM lcl_syntax_highlighter FINAL. PUBLIC SECTION. METHODS constructor. CONSTANTS: BEGIN OF c_css, xml_tag TYPE string VALUE 'xml_tag', "#EC NOTEXT attr TYPE string VALUE 'attr', "#EC NOTEXT attr_val TYPE string VALUE 'attr_val', "#EC NOTEXT END OF c_css, BEGIN OF c_token, xml_tag TYPE c VALUE 'X', "#EC NOTEXT attr TYPE c VALUE 'A', "#EC NOTEXT attr_val TYPE c VALUE 'V', "#EC NOTEXT END OF c_token, 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 END OF c_regex. PROTECTED SECTION. METHODS order_matches REDEFINITION. ENDCLASS. " lcl_syntax_xml DEFINITION *----------------------------------------------------------------------* * CLASS lcl_syntax_highlighter IMPLEMENTATION *----------------------------------------------------------------------* * Implementation of syntax highligther for ABAP source code *----------------------------------------------------------------------* CLASS lcl_syntax_highlighter IMPLEMENTATION. METHOD create. " Create instance of highighter dynamically dependent on syntax type IF iv_filename CP '*.abap'. CREATE OBJECT ro_instance TYPE lcl_syntax_abap. ELSEIF iv_filename CP '*.xml'. CREATE OBJECT ro_instance TYPE lcl_syntax_xml. ELSE. CLEAR ro_instance. ENDIF. ENDMETHOD. " create. METHOD add_rule. DATA ls_rule LIKE LINE OF mt_rules. CREATE OBJECT ls_rule-regex EXPORTING pattern = iv_regex ignore_case = abap_true. ls_rule-token = iv_token. ls_rule-style = iv_style. APPEND ls_rule TO mt_rules. ENDMETHOD. METHOD parse_line. DATA: lo_regex TYPE REF TO cl_abap_regex, lo_matcher TYPE REF TO cl_abap_matcher, lt_result TYPE match_result_tab, ls_match TYPE ty_match. FIELD-SYMBOLS: LIKE LINE OF mt_rules, TYPE match_result. CLEAR et_matches. " Process syntax-dependent regex table and find all matches LOOP AT mt_rules ASSIGNING . lo_regex = -regex. lo_matcher = lo_regex->create_matcher( text = iv_line ). lt_result = lo_matcher->find_all( ). " 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. ENDLOOP. ENDLOOP. ENDMETHOD. " parse_line METHOD extend_matches. DATA: lv_line_len TYPE i, lv_last_pos TYPE i VALUE 0, lv_length TYPE i, ls_match TYPE ty_match. FIELD-SYMBOLS TYPE ty_match. lv_line_len = strlen( iv_line ). SORT ct_matches BY offset. " Add entries refering to parts of text that should not be formatted LOOP AT ct_matches ASSIGNING . IF -offset > lv_last_pos. lv_length = -offset - lv_last_pos. ls_match-token = c_token_none. ls_match-offset = lv_last_pos. ls_match-length = lv_length. INSERT ls_match INTO ct_matches INDEX sy-tabix. ENDIF. lv_last_pos = -offset + -length. ENDLOOP. " Add remainder of the string IF lv_line_len > lv_last_pos. lv_length = lv_line_len - lv_last_pos. ls_match-token = c_token_none. ls_match-offset = lv_last_pos. ls_match-length = lv_length. APPEND ls_match TO ct_matches. ENDIF. ENDMETHOD. " extend_matches METHOD format_line. DATA: lv_chunk TYPE string, ls_rule LIKE LINE OF mt_rules. FIELD-SYMBOLS TYPE ty_match. LOOP AT it_matches ASSIGNING . lv_chunk = substring( val = iv_line off = -offset len = -length ). CLEAR ls_rule. " Failed read equals no style READ TABLE mt_rules INTO ls_rule WITH KEY token = -token. lv_chunk = me->apply_style( iv_line = lv_chunk iv_class = ls_rule-style ). rv_line = rv_line && lv_chunk. ENDLOOP. ENDMETHOD. " format_line METHOD apply_style. DATA lv_escaped TYPE string. lv_escaped = escape( val = iv_line format = cl_abap_format=>e_html_attr ). IF iv_class IS NOT INITIAL. rv_line = |{ lv_escaped }|. ELSE. rv_line = lv_escaped. ENDIF. ENDMETHOD. " apply_style METHOD process_line. DATA: lt_matches TYPE ty_match_tt. IF strlen( iv_line ) = 0. RETURN. ENDIF. me->parse_line( EXPORTING iv_line = iv_line IMPORTING et_matches = lt_matches ). me->order_matches( EXPORTING iv_line = iv_line CHANGING ct_matches = lt_matches ). me->extend_matches( EXPORTING iv_line = iv_line CHANGING ct_matches = lt_matches ). rv_line = me->format_line( iv_line = iv_line it_matches = lt_matches ). ENDMETHOD. " process_line ENDCLASS. " lcl_syntax_highlighter IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_syntax_abap IMPLEMENTATION *----------------------------------------------------------------------* * Implementation of syntax highligther for XML source code *----------------------------------------------------------------------* CLASS lcl_syntax_abap IMPLEMENTATION. METHOD class_constructor. init_keywords( ). ENDMETHOD. " class_constructor METHOD is_keyword. DATA lv_str TYPE string. lv_str = to_upper( iv_chunk ). READ TABLE gt_keywords WITH KEY table_line = lv_str TRANSPORTING NO FIELDS. rv_yes = boolc( sy-subrc = 0 ). ENDMETHOD. " is_keyword. METHOD constructor. super->constructor( ). " Initialize instances of regular expression add_rule( iv_regex = c_regex-keyword iv_token = c_token-keyword iv_style = c_css-keyword ). add_rule( iv_regex = c_regex-comment iv_token = c_token-comment iv_style = c_css-comment ). add_rule( iv_regex = c_regex-text iv_token = c_token-text iv_style = c_css-text ). ENDMETHOD. " constructor METHOD init_keywords. DATA: lv_keywords TYPE string, lt_keywords TYPE STANDARD TABLE OF string. lv_keywords = '&&|?TO|ABAP-SOURCE|ABBREVIATED|ABS|ABSTRACT|ACCEPT|ACCEPTING|ACCESSPOLICY' && '|ACCORDING|ACOS|ACTIVATION|ACTUAL|ADD|ADD-CORRESPONDING|ADJACENT|AFTER|ALIAS' && '|ALIASES|ALIGN|ALL|ALLOCATE|ALPHA|ANALYSIS|ANALYZER|AND|ANY|APPEND|APPENDAGE' && '|APPENDING|APPLICATION|ARCHIVE|AREA|ARITHMETIC|AS|ASCENDING|ASIN|ASPECT|ASSERT' && '|ASSIGN|ASSIGNED|ASSIGNING|ASSOCIATION|ASYNCHRONOUS|AT|ATAN|ATTRIBUTES|AUTHORITY' && '|AUTHORITY-CHECK|AVG|BACK|BACKGROUND|BACKUP|BACKWARD|BADI|BASE|BEFORE|BEGIN' && '|BETWEEN|BIG|BINARY|BINDING|BIT|BIT-AND|BIT-NOT|BIT-OR|BIT-XOR|BLACK|BLANK' && '|BLANKS|BLOB|BLOCK|BLOCKS|BLUE|BOUND|BOUNDARIES|BOUNDS|BOXED|BREAK-POINT|BT' && '|BUFFER|BY|BYPASSING|BYTE|BYTE-CA|BYTE-CN|BYTE-CO|BYTE-CS|BYTE-NA|BYTE-NS' && '|BYTE-ORDER|C|CA|CALL|CALLING|CASE|CAST|CASTING|CATCH|CEIL|CENTER|CENTERED' && '|CHAIN|CHAIN-INPUT|CHAIN-REQUEST|CHANGE|CHANGING|CHANNELS|CHARACTER|CHARLEN' && '|CHAR-TO-HEX|CHECK|CHECKBOX|CI_|CIRCULAR|CLASS|CLASS-CODING|CLASS-DATA' && '|CLASS-EVENTS|CLASS-METHODS|CLASS-POOL|CLEANUP|CLEAR|CLIENT|CLOB|CLOCK|CLOSE' && '|CN|CNT|CO|COALESCE|CODE|CODING|COL_BACKGROUND|COL_GROUP|COL_HEADING|COL_KEY' && '|COL_NEGATIVE|COL_NORMAL|COL_POSITIVE|COL_TOTAL|COLLECT|COLOR|COLUMN|COLUMNS' && '|COMMENT|COMMENTS|COMMIT|COMMON|COMMUNICATION|COMPARING|COMPONENT|COMPONENTS' && '|COMPRESSION|COMPUTE|CONCAT|CONCATENATE|COND|CONDENSE|CONDITION|CONNECT' && '|CONNECTION|CONSTANTS|CONTEXT|CONTEXTS|CONTINUE|CONTROL|CONTROLS|CONV|CONVERSION' && '|CONVERT|COPIES|COPY|CORRESPONDING|COS|COSH|COUNT|COUNTRY|COVER|CP|CPI|CREATE' && '|CREATING|CRITICAL|CS|CURRENCY|CURRENCY_CONVERSION|CURRENT|CURSOR|CURSOR-SELECTION' && '|CUSTOMER|CUSTOMER-FUNCTION|DANGEROUS|DATA|DATABASE|DATAINFO|DATASET|DATE' && '|DAYLIGHT|DBMAXLEN|DD/MM/YY|DD/MM/YYYY|DDMMYY|DEALLOCATE|DECIMAL_SHIFT|DECIMALS' && '|DECLARATIONS|DEEP|DEFAULT|DEFERRED|DEFINE|DEFINING|DEFINITION|DELETE|DELETING' && '|DEMAND|DEPARTMENT|DESCENDING|DESCRIBE|DESTINATION|DETAIL|DIALOG|DIRECTORY' && '|DISCONNECT|DISPLAY|DISPLAY-MODE|DISTANCE|DISTINCT|DIV|DIVIDE|DIVIDE-CORRESPONDING' && '|DIVISION|DO|DUMMY|DUPLICATE|DUPLICATES|DURATION|DURING|DYNAMIC|DYNPRO|E|EACH' && '|EDIT|EDITOR-CALL|ELSE|ELSEIF|EMPTY|ENABLED|ENABLING|ENCODING|END|ENDAT|ENDCASE' && '|ENDCATCH|ENDCHAIN|ENDCLASS|ENDDO|ENDENHANCEMENT|END-ENHANCEMENT-SECTION' && '|ENDEXEC|ENDFOR|ENDFORM|ENDFUNCTION|ENDIAN|ENDIF|ENDING|ENDINTERFACE' && '|END-LINES|ENDLOOP|ENDMETHOD|ENDMODULE|END-OF-DEFINITION|END-OF-FILE' && '|END-OF-PAGE|END-OF-SELECTION|ENDON|ENDPROVIDE|ENDSELECT|ENDTRY|ENDWHILE' && '|ENGINEERING|ENHANCEMENT|ENHANCEMENT-POINT|ENHANCEMENTS|ENHANCEMENT-SECTION' && '|ENTRIES|ENTRY|ENVIRONMENT|EQ|EQUAL|EQUIV|ERRORMESSAGE|ERRORS|ESCAPE|ESCAPING' && '|EVENT|EVENTS|EXACT|EXCEPT|EXCEPTION|EXCEPTIONS|EXCEPTION-TABLE|EXCLUDE|EXCLUDING' && '|EXEC|EXECUTE|EXISTS|EXIT|EXIT-COMMAND|EXP|EXPAND|EXPANDING|EXPIRATION|EXPLICIT' && '|EXPONENT|EXPORT|EXPORTING|EXTEND|EXTENDED|EXTENSION|EXTRACT|FAIL|FETCH|FIELD' && '|FIELD-GROUPS|FIELDS|FIELD-SYMBOL|FIELD-SYMBOLS|FILE|FILTER|FILTERS|FILTER-TABLE' && '|FINAL|FIND|FIRST|FIRST-LINE|FIXED-POINT|FKEQ|FKGE|FLOOR|FLUSH|FONT|FOR|FORM' && '|FORMAT|FORWARD|FOUND|FRAC|FRAME|FRAMES|FREE|FRIENDS|FROM|FUNCTION|FUNCTIONALITY' && '|FUNCTION-POOL|FURTHER|GAPS|GE|GENERATE|GET|GIVING|GKEQ|GKGE|GLOBAL|GRANT|GREATER' && '|GREEN|GROUP|GROUPS|GT|HANDLE|HANDLER|HARMLESS|HASHED|HAVING|HDB|HEADER|HEADERS' && '|HEADING|HEAD-LINES|HELP-ID|HELP-REQUEST|HIDE|HIGH|HINT|HOLD|HOTSPOT|I|ICON|ID' && '|IDENTIFICATION|IDENTIFIER|IDS|IF|IGNORE|IGNORING|IMMEDIATELY|IMPLEMENTATION' && '|IMPLEMENTATIONS|IMPLEMENTED|IMPLICIT|IMPORT|IMPORTING|IN|INACTIVE|INCL|INCLUDE' && '|INCLUDES|INCLUDING|INCREMENT|INDEX|INDEX-LINE|INFOTYPES|INHERITING|INIT|INITIAL' && '|INITIALIZATION|INNER|INOUT|INPUT|INSERT|INSTANCES|INTENSIFIED|INTERFACE' && '|INTERFACE-POOL|INTERFACES|INTERNAL|INTERVALS|INTO|INVERSE|INVERTED-DATE|IS' && '|ISO|ITERATOR|ITNO|JOB|JOIN|KEEP|KEEPING|KERNEL|KEY|KEYS|KEYWORDS|KIND' && '|LANGUAGE|LAST|LATE|LAYOUT|LE|LEADING|LEAVE|LEFT|LEFT-JUSTIFIED|LEFTPLUS' && '|LEFTSPACE|LEGACY|LENGTH|LESS|LET|LEVEL|LEVELS|LIKE|LINE|LINE-COUNT|LINEFEED' && '|LINES|LINE-SELECTION|LINE-SIZE|LIST|LISTBOX|LIST-PROCESSING|LITTLE|LLANG' && '|LOAD|LOAD-OF-PROGRAM|LOB|LOCAL|LOCALE|LOCATOR|LOG|LOG10|LOGFILE|LOGICAL' && '|LOG-POINT|LONG|LOOP|LOW|LOWER|LPAD|LPI|LT|M|MAIL|MAIN|MAJOR-ID|MAPPING|MARGIN' && '|MARK|MASK|MATCH|MATCHCODE|MAX|MAXIMUM|MEDIUM|MEMBERS|MEMORY|MESH|MESSAGE' && '|MESSAGE-ID|MESSAGES|MESSAGING|METHOD|METHODS|MIN|MINIMUM|MINOR-ID|MM/DD/YY' && '|MM/DD/YYYY|MMDDYY|MOD|MODE|MODIF|MODIFIER|MODIFY|MODULE|MOVE|MOVE-CORRESPONDING' && '|MULTIPLY|MULTIPLY-CORRESPONDING|NA|NAME|NAMETAB|NATIVE|NB|NE|NESTED|NESTING' && '|NEW|NEW-LINE|NEW-PAGE|NEW-SECTION|NEXT|NO|NODE|NODES|NO-DISPLAY' && '|NO-EXTENSION|NO-GAP|NO-GAPS|NO-GROUPING|NO-HEADING|NON-UNICODE|NON-UNIQUE' && '|NO-SCROLLING|NO-SIGN|NOT|NO-TITLE|NO-TOPOFPAGE|NO-ZERO|NP|NS|NULL|NUMBER' && '|NUMOFCHAR|O|OBJECT|OBJECTS|OBLIGATORY|OCCURRENCE|OCCURRENCES|OCCURS|OF|OFF' && '|OFFSET|OLE|ON|ONLY|OPEN|OPTION|OPTIONAL|OPTIONS|OR|ORDER|OTHER|OTHERS|OUT' && '|OUTER|OUTPUT|OUTPUT-LENGTH|OVERFLOW|OVERLAY|PACK|PACKAGE|PAD|PADDING|PAGE' && '|PAGES|PARAMETER|PARAMETERS|PARAMETER-TABLE|PART|PARTIALLY|PATTERN|PERCENTAGE' && '|PERFORM|PERFORMING|PERSON|PF|PF-STATUS|PINK|PLACES|POOL|POS_HIGH|POS_LOW' && '|POSITION|PRAGMAS|PRECOMPILED|PREFERRED|PRESERVING|PRIMARY|PRINT|PRINT-CONTROL' && '|PRIORITY|PRIVATE|PROCEDURE|PROCESS|PROGRAM|PROPERTY|PROTECTED|PROVIDE|PUBLIC' && '|PUSHBUTTON|PUT|QUEUE-ONLY|QUICKINFO|RADIOBUTTON|RAISE|RAISING|RANGE|RANGES' && '|RAW|READ|READER|READ-ONLY|RECEIVE|RECEIVED|RECEIVER|RECEIVING|RED|REDEFINITION' && '|REDUCE|REDUCED|REF|REFERENCE|REFRESH|REGEX|REJECT|REMOTE|RENAMING|REPLACE' && '|REPLACEMENT|REPLACING|REPORT|REQUEST|REQUESTED|RESERVE|RESET|RESOLUTION' && '|RESPECTING|RESPONSIBLE|RESULT|RESULTS|RESUMABLE|RESUME|RETRY|RETURN|RETURNCODE' && '|RETURNING|RIGHT|RIGHT-JUSTIFIED|RIGHTPLUS|RIGHTSPACE|RISK|RMC_COMMUNICATION_FAILURE' && '|RMC_INVALID_STATUS|RMC_SYSTEM_FAILURE|ROLE|ROLLBACK|ROUND|ROWS|RTTI|RUN|SAP' && '|SAP-SPOOL|SAVING|SCALE_PRESERVING|SCALE_PRESERVING_SCIENTIFIC|SCAN|SCIENTIFIC' && '|SCIENTIFIC_WITH_LEADING_ZERO|SCREEN|SCROLL|SCROLL-BOUNDARY|SCROLLING|SEARCH' && '|SECONDARY|SECONDS|SECTION|SELECT|SELECTION|SELECTIONS|SELECTION-SCREEN|SELECTION-SET' && '|SELECTION-SETS|SELECTION-TABLE|SELECT-OPTIONS|SELECTOR|SEND|SEPARATE|SEPARATED|SET' && '|SHARED|SHIFT|SHORT|SHORTDUMP-ID|SIGN|SIGN_AS_POSTFIX|SIMPLE|SIN|SINGLE|SINH|SIZE' && '|SKIP|SKIPPING|SMART|SOME|SORT|SORTABLE|SORTED|SOURCE|SPACE|SPECIFIED|SPLIT|SPOOL' && '|SPOTS|SQL|SQLSCRIPT|SQRT|STABLE|STAMP|STANDARD|STARTING|START-OF-SELECTION|STATE' && '|STATEMENT|STATEMENTS|STATIC|STATICS|STATUSINFO|STEP-LOOP|STOP|STRLEN|STRUCTURE' && '|STRUCTURES|STYLE|SUBKEY|SUBMATCHES|SUBMIT|SUBROUTINE|SUBSCREEN|SUBSTRING|SUBTRACT' && '|SUBTRACT-CORRESPONDING|SUFFIX|SUM|SUMMARY|SUMMING|SUPPLIED|SUPPLY|SUPPRESS|SWITCH' && '|SWITCHSTATES|SYMBOL|SYNCPOINTS|SYNTAX|SYNTAX-CHECK|SYNTAX-TRACE' && '|SYSTEM-CALL|SYSTEM-EXCEPTIONS|SYSTEM-EXIT|TAB|TABBED|TABLE|TABLES|TABLEVIEW|TABSTRIP' && '|TAN|TANH|TARGET|TASK|TASKS|TEST|TESTING|TEXT|TEXTPOOL|THEN|THROW|TIME|TIMES|TIMESTAMP' && '|TIMEZONE|TITLE|TITLEBAR|TITLE-LINES|TO|TOKENIZATION|TOKENS|TOP-LINES|TOP-OF-PAGE' && '|TRACE-FILE|TRACE-TABLE|TRAILING|TRANSACTION|TRANSFER|TRANSFORMATION|TRANSLATE' && '|TRANSPORTING|TRMAC|TRUNC|TRUNCATE|TRUNCATION|TRY|TYPE|TYPE-POOL|TYPE-POOLS|TYPES' && '|ULINE|UNASSIGN|UNDER|UNICODE|UNION|UNIQUE|UNIT|UNIT_CONVERSION|UNIX|UNPACK|UNTIL' && '|UNWIND|UP|UPDATE|UPPER|USER|USER-COMMAND|USING|UTF-8|VALID|VALUE|VALUE-REQUEST|VALUES' && '|VARY|VARYING|VERIFICATION-MESSAGE|VERSION|VIA|VIEW|VISIBLE|WAIT|WARNING|WHEN|WHENEVER' && '|WHERE|WHILE|WIDTH|WINDOW|WINDOWS|WITH|WITH-HEADING|WITHOUT|WITH-TITLE|WORD|WORK' && '|WRITE|WRITER|X|XML|XOR|XSD|XSTRLEN|YELLOW|YES|YYMMDD|Z|ZERO|ZONE'. SPLIT lv_keywords AT '|' INTO TABLE lt_keywords. gt_keywords = lt_keywords. " Hash table ENDMETHOD. " init_keywords METHOD parse_line. "REDEFINITION DATA lv_index TYPE i. FIELD-SYMBOLS LIKE LINE OF et_matches. super->parse_line( EXPORTING iv_line = iv_line IMPORTING et_matches = et_matches ). " Remove non-keywords LOOP AT et_matches ASSIGNING WHERE token = c_token-keyword. lv_index = sy-tabix. IF abap_false = is_keyword( substring( val = iv_line off = -offset len = -length ) ). DELETE et_matches INDEX lv_index. ENDIF. ENDLOOP. ENDMETHOD. " parse_line. METHOD order_matches. DATA: lv_index TYPE sy-tabix, lv_line_len TYPE i, lv_prev_token TYPE c. FIELD-SYMBOLS: TYPE ty_match, TYPE ty_match. SORT ct_matches BY offset. lv_line_len = strlen( iv_line ). LOOP AT ct_matches ASSIGNING . lv_index = sy-tabix. " Delete matches after open text match IF lv_prev_token = c_token-text AND -token <> c_token-text. DELETE ct_matches INDEX lv_index. CONTINUE. ENDIF. CASE -token. WHEN c_token-keyword. IF -offset > 0. " Delete match if keyword is part of structure or field symbol IF substring( val = iv_line off = ( -offset - 1 ) len = 1 ) CA '-<'. DELETE ct_matches INDEX lv_index. CONTINUE. ENDIF. ENDIF. WHEN c_token-comment. -length = lv_line_len - -offset. DELETE ct_matches FROM lv_index + 1. CONTINUE. WHEN c_token-text. -text_tag = substring( val = iv_line off = -offset len = -length ). IF lv_prev_token = c_token-text. IF -text_tag = -text_tag. -length = -offset + -length - -offset. CLEAR lv_prev_token. ELSEIF -text_tag = '}' AND -text_tag = '{'. -length = -offset - -offset - 1. " Shift } out of scope -offset = -offset + 1. " Shift { out of scope CLEAR lv_prev_token. ELSEIF -text_tag = '{'. -length = -offset - -offset. CLEAR lv_prev_token. ELSEIF -text_tag = '}'. -length = -offset - -offset. -offset = -offset + 1. " Shift } out of scope CLEAR lv_prev_token. ENDIF. DELETE ct_matches INDEX lv_index. CONTINUE. ENDIF. ENDCASE. lv_prev_token = -token. ASSIGN TO . ENDLOOP. ENDMETHOD. " order_matches. ENDCLASS. " lcl_syntax_abap IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_syntax_xml IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_syntax_xml IMPLEMENTATION. METHOD constructor. super->constructor( ). " 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-attr iv_token = c_token-attr iv_style = c_css-attr ). add_rule( iv_regex = c_regex-attr_val iv_token = c_token-attr_val iv_style = c_css-attr_val ). ENDMETHOD. METHOD order_matches. DATA: lv_index TYPE sy-tabix, lv_prev_token TYPE c, lv_state TYPE c VALUE 'O'. " O - for open tag; C - for closed tag; FIELD-SYMBOLS: TYPE ty_match, TYPE ty_match. SORT ct_matches BY offset. LOOP AT ct_matches ASSIGNING . lv_index = sy-tabix. CASE -token. WHEN c_token-xml_tag. -text_tag = substring( val = iv_line off = -offset len = -length ). " No other matches between two tags IF -text_tag = '>' AND lv_prev_token = c_token-xml_tag. lv_state = 'C'. -length = -offset - -offset + -length. DELETE ct_matches INDEX lv_index. CONTINUE. " Adjust length and offset of closing tag ELSEIF -text_tag = '>' AND lv_prev_token <> c_token-xml_tag. lv_state = 'C'. -length = -offset - -offset - -length + -length. -offset = -offset + -length. ELSE. lv_state = 'O'. ENDIF. WHEN OTHERS. IF lv_prev_token = c_token-xml_tag. -length = -offset - -offset. " Extend length of the opening tag ENDIF. IF lv_state = 'C'. " Delete all matches between tags DELETE ct_matches INDEX lv_index. CONTINUE. ENDIF. ENDCASE. lv_prev_token = -token. ASSIGN TO . ENDLOOP. ENDMETHOD. " order_matches ENDCLASS. " lcl_syntax_xml IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltcl_syntax_cases definition *----------------------------------------------------------------------* CLASS ltcl_syntax_cases DEFINITION FINAL FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PRIVATE SECTION. DATA: mt_after_parse TYPE lcl_syntax_highlighter=>ty_match_tt, mt_after_order TYPE lcl_syntax_highlighter=>ty_match_tt, mt_after_extend TYPE lcl_syntax_highlighter=>ty_match_tt, ms_match TYPE lcl_syntax_highlighter=>ty_match. METHODS: do_test IMPORTING iv_line TYPE string iv_filename TYPE string, test_abap_01 FOR TESTING, test_abap_02 FOR TESTING, test_abap_03 FOR TESTING, test_abap_04 FOR TESTING, test_abap_05 FOR TESTING, test_abap_06 FOR TESTING, test_abap_07 FOR TESTING, test_abap_08 FOR TESTING, test_xml_01 FOR TESTING, test_xml_02 FOR TESTING, test_xml_03 FOR TESTING, test_xml_04 FOR TESTING, test_xml_05 FOR TESTING. ENDCLASS. " ltcl_syntax_cases *----------------------------------------------------------------------* * CLASS ltcl_syntax_cases IMPLEMENTATION *----------------------------------------------------------------------* CLASS ltcl_syntax_cases IMPLEMENTATION. DEFINE _generate_parse. ms_match-token = &1. ms_match-offset = &2. ms_match-length = &3. APPEND ms_match TO mt_after_parse. END-OF-DEFINITION. " _generate_parse DEFINE _generate_order. ms_match-token = &1. ms_match-offset = &2. ms_match-length = &3. ms_match-text_tag = &4. APPEND ms_match TO mt_after_order. END-OF-DEFINITION. " _generate_order DEFINE _generate_extend. ms_match-token = &1. ms_match-offset = &2. ms_match-length = &3. ms_match-text_tag = &4. APPEND ms_match TO mt_after_extend. END-OF-DEFINITION. " _generate_extend METHOD do_test. DATA: lt_matches_act TYPE lcl_syntax_highlighter=>ty_match_tt, lo TYPE REF TO lcl_syntax_highlighter. lo = lcl_syntax_highlighter=>create( iv_filename ). lo->parse_line( EXPORTING iv_line = iv_line IMPORTING et_matches = lt_matches_act ). SORT lt_matches_act BY offset. cl_abap_unit_assert=>assert_equals( exp = mt_after_parse act = lt_matches_act msg = | Error during parsing: { iv_line }| ). lo->order_matches( EXPORTING iv_line = iv_line CHANGING ct_matches = lt_matches_act ). cl_abap_unit_assert=>assert_equals( exp = mt_after_order act = lt_matches_act msg = | Error during ordering: { iv_line }| ). lo->extend_matches( EXPORTING iv_line = iv_line CHANGING ct_matches = lt_matches_act ). cl_abap_unit_assert=>assert_equals( exp = mt_after_extend act = lt_matches_act msg = | Error during extending: { iv_line }| ). ENDMETHOD. "test ****************************************************** * Test parsing and ordering of comments * ****************************************************** METHOD test_abap_01. DATA lv_line TYPE string. lv_line = '* commented out line with key word data'. "#EC NOTEXT " Generate table with expected values after parsing _generate_parse 'C' 0 1. _generate_parse 'K' 12 3. _generate_parse 'K' 16 4. _generate_parse 'K' 21 4. _generate_parse 'K' 26 3. _generate_parse 'K' 30 4. _generate_parse 'K' 35 4. " Generate table with expected values after ordering _generate_order 'C' 0 39 ''. " Generate table with expected values after ordering _generate_extend 'C' 0 39 ''. do_test( iv_line = lv_line iv_filename = '*.abap' ). ENDMETHOD. " test_abap_01 ****************************************************** * Test parsing and ordering of remainder of string * ****************************************************** METHOD test_abap_02. DATA lv_line TYPE string. lv_line = 'data: lv_var_name type string.'. "#EC NOTEXT " Generate table with expected values after parsing _generate_parse 'K' 0 4. _generate_parse 'K' 18 4. " Generate table with expected values after ordering _generate_order 'K' 0 4 ''. _generate_order 'K' 18 4 ''. " Generate table with expected values after extending _generate_extend 'K' 0 4 ''. _generate_extend '.' 4 14 ''. _generate_extend 'K' 18 4 ''. _generate_extend '.' 22 8 ''. do_test( iv_line = lv_line iv_filename = '*.abap' ). ENDMETHOD. " test_abap_02 ****************************************************** * Test parsing and ordering of key words & texts * ****************************************************** METHOD test_abap_03. DATA lv_line TYPE string. lv_line = 'call function ''FM_NAME''. " Commented'. "#EC NOTEXT " Generate table with expected values after parsing _generate_parse 'K' 0 4. _generate_parse 'K' 5 8. _generate_parse 'T' 14 1. _generate_parse 'T' 22 1. _generate_parse 'C' 25 1. " Generate table with expected values after ordering _generate_order 'K' 0 4 ''. _generate_order 'K' 5 8 ''. _generate_order 'T' 14 9 ''''. _generate_order 'C' 25 11 ''. " Generate table with expected values after extending _generate_extend 'K' 0 4 ''. _generate_extend '.' 4 1 ''. _generate_extend 'K' 5 8 ''. _generate_extend '.' 13 1 ''. _generate_extend 'T' 14 9 ''''. _generate_extend '.' 23 2 ''. _generate_extend 'C' 25 11 ''. do_test( iv_line = lv_line iv_filename = '*.abap' ). ENDMETHOD. " test_abap_03 ****************************************************** * Test parsing and ordering of key words in texts * ****************************************************** METHOD test_abap_04. DATA lv_line TYPE string. lv_line = 'constants: lc_var type string value ''simpletext data simpletext''.'. "#EC NOTEXT " Generate table with expected values after parsing _generate_parse 'K' 0 9. _generate_parse 'K' 18 4. _generate_parse 'K' 30 5. _generate_parse 'T' 36 1. _generate_parse 'K' 48 4. _generate_parse 'T' 63 1. " Generate table with expected values after ordering _generate_order 'K' 0 9 ''. _generate_order 'K' 18 4 ''. _generate_order 'K' 30 5 ''. _generate_order 'T' 36 28 ''''. " Generate table with expected values after ordering _generate_extend 'K' 0 9 ''. _generate_extend '.' 9 9 ''. _generate_extend 'K' 18 4 ''. _generate_extend '.' 22 8 ''. _generate_extend 'K' 30 5 ''. _generate_extend '.' 35 1 ''. _generate_extend 'T' 36 28 ''''. _generate_extend '.' 64 1 ''. do_test( iv_line = lv_line iv_filename = '*.abap' ). ENDMETHOD. " test_abap_04 ****************************************************** * Test parsing and ordering texts in curly brackets * ****************************************************** METHOD test_abap_05. DATA lv_line TYPE string. lv_line = 'a = |{ b }={ c }|.'. "#EC NOTEXT " Generate table with expected values after parsing _generate_parse 'T' 4 1. _generate_parse 'T' 5 1. _generate_parse 'T' 9 1. _generate_parse 'T' 11 1. _generate_parse 'K' 13 1. _generate_parse 'T' 15 1. _generate_parse 'T' 16 1. " Generate table with expected values after ordering _generate_order 'T' 4 1 '|'. _generate_order 'T' 10 1 '}'. _generate_order 'K' 13 1 ''. _generate_order 'T' 16 1 '}'. " Generate table with expected values after extending _generate_extend '.' 0 4 ''. _generate_extend 'T' 4 1 '|'. _generate_extend '.' 5 5 ''. _generate_extend 'T' 10 1 '}'. _generate_extend '.' 11 2 ''. _generate_extend 'K' 13 1 ''. _generate_extend '.' 14 2 ''. _generate_extend 'T' 16 1 '}'. _generate_extend '.' 17 1 ''. do_test( iv_line = lv_line iv_filename = '*.abap' ). ENDMETHOD. " test_abap_05 ****************************************************** * Test parsing and ordering of texts * ****************************************************** METHOD test_abap_06. DATA lv_line TYPE string. lv_line = 'lv_line = lc_constant && |XYZ { ''ab'' && |ac{ ''UU'' }| }|'. "#EC NOTEXT " Generate table with expected values after parsing _generate_parse 'K' 22 2. _generate_parse 'T' 25 1. _generate_parse 'T' 30 1. _generate_parse 'T' 32 1. _generate_parse 'T' 35 1. _generate_parse 'K' 37 2. _generate_parse 'T' 40 1. _generate_parse 'T' 43 1. _generate_parse 'T' 45 1. _generate_parse 'T' 48 1. _generate_parse 'T' 50 1. _generate_parse 'T' 51 1. _generate_parse 'T' 53 1. _generate_parse 'T' 54 1. " Generate table with expected values after ordering _generate_order 'K' 22 2 ''. _generate_order 'T' 25 5 '|'. _generate_order 'T' 32 4 ''''. _generate_order 'K' 37 2 ''. _generate_order 'T' 40 3 '|'. _generate_order 'T' 45 4 ''''. _generate_order 'T' 51 1 '}'. _generate_order 'T' 54 1 '}'. " Generate table with expected values after extending _generate_extend '.' 00 22 ''. _generate_extend 'K' 22 2 ''. _generate_extend '.' 24 1 ''. _generate_extend 'T' 25 5 '|'. _generate_extend '.' 30 2 ''. _generate_extend 'T' 32 4 ''''. _generate_extend '.' 36 1 ''. _generate_extend 'K' 37 2 ''. _generate_extend '.' 39 1 ''. _generate_extend 'T' 40 3 '|'. _generate_extend '.' 43 2 ''. _generate_extend 'T' 45 4 ''''. _generate_extend '.' 49 2 ''. _generate_extend 'T' 51 1 '}'. _generate_extend '.' 52 2 ''. _generate_extend 'T' 54 1 '}'. do_test( iv_line = lv_line iv_filename = '*.abap' ). ENDMETHOD. " test_abap_06 ******************************************************** * Check that '*' in select statement is not a match * ******************************************************** METHOD test_abap_07. DATA lv_line TYPE string. lv_line = 'SELECT * FROM foo'. "#EC NOTEXT " Generate table with expected values after parsing _generate_parse 'K' 0 6. _generate_parse 'K' 9 4. " Generate table with expected values after ordering _generate_order 'K' 0 6 ''. _generate_order 'K' 9 4 ''. " Generate table with expected values after extending _generate_extend 'K' 0 6 ''. _generate_extend '.' 6 3 ''. _generate_extend 'K' 9 4 ''. _generate_extend '.' 13 4 ''. do_test( iv_line = lv_line iv_filename = '*.abap' ). ENDMETHOD. " test_abap_07 ******************************************************** * Test parsing and ordering of key words in structures * ******************************************************** METHOD test_abap_08. DATA lv_line TYPE string. lv_line = 'lv_length = -length.'. "#EC NOTEXT " Generate table with expected values after parsing _generate_parse 'K' 13 5. _generate_parse 'K' 20 6. " Generate table with expected values after extending _generate_extend '.' 0 27 ''. do_test( iv_line = lv_line iv_filename = '*.abap' ). ENDMETHOD. " test_abap_08 ******************************************************** * Test parsing and ordering of tags in xml * ******************************************************** METHOD test_xml_01. DATA lv_line TYPE string. lv_line = 'Text'. "#EC NOTEXT " Generate table with expected values after parsing _generate_parse 'X' 0 1. _generate_parse 'X' 4 1. _generate_parse 'X' 9 1. _generate_parse 'X' 14 1. " Generate table with expected values after ordering _generate_order 'X' 0 5 '<'. _generate_order 'X' 9 6 '<'. " Generate table with expected values after extending _generate_extend 'X' 0 5 '<'. _generate_extend '.' 5 4 ''. _generate_extend 'X' 9 6 '<'. do_test( iv_line = lv_line iv_filename = '*.xml' ). ENDMETHOD. " test_xml_01 METHOD test_xml_02. DATA lv_line TYPE string. lv_line = ''. "#EC NOTEXT " Generate table with expected values after parsing _generate_parse 'X' 0 1. _generate_parse 'X' 5 1. " Generate table with expected values after ordering _generate_order 'X' 0 6 '<'. " Generate table with expected values after extending _generate_extend 'X' 0 6 '<'. do_test( iv_line = lv_line iv_filename = '*.xml' ). ENDMETHOD. " test_xml_02 METHOD test_xml_03. DATA lv_line TYPE string. 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. " test_xml_03 METHOD test_xml_04. DATA lv_line TYPE string. lv_line = ''. "#EC NOTEXT " Generate table with expected values after parsing _generate_parse 'X' 0 1. _generate_parse 'A' 5 8. _generate_parse 'V' 14 5. _generate_parse 'X' 20 1. " Generate table with expected values after ordering _generate_order 'X' 0 5 '<'. _generate_order 'A' 5 8 ''. _generate_order 'V' 14 5 ''. _generate_order 'X' 19 2 '>'. " Generate table with expected values after extending _generate_extend 'X' 0 5 '<'. _generate_extend 'A' 5 8 ''. _generate_extend '.' 13 1 ''. _generate_extend 'V' 14 5 ''. _generate_extend 'X' 19 2 '>'. do_test( iv_line = lv_line iv_filename = '*.xml' ). ENDMETHOD. " test_xml_04 METHOD test_xml_05. DATA lv_line TYPE string. lv_line = '"text"'. "#EC NOTEXT " Generate table with expected values after parsing _generate_parse 'X' 0 1. _generate_parse 'A' 7 6. _generate_parse 'V' 14 4. _generate_parse 'A' 18 6. _generate_parse 'V' 25 4. _generate_parse 'X' 29 1. _generate_parse 'V' 30 6. _generate_parse 'X' 36 1. _generate_parse 'X' 44 1. " Generate table with expected values after ordering _generate_order 'X' 0 7 '<'. _generate_order 'A' 7 6 ''. _generate_order 'V' 14 4 ''. _generate_order 'A' 18 6 ''. _generate_order 'V' 25 4 ''. _generate_order 'X' 29 1 '>'. _generate_order 'X' 36 9 '<'. " Generate table with expected values after extending _generate_extend 'X' 0 7 '<'. _generate_extend 'A' 7 6 ''. _generate_extend '.' 13 1 ''. _generate_extend 'V' 14 4 ''. _generate_extend 'A' 18 6 ''. _generate_extend '.' 24 1 ''. _generate_extend 'V' 25 4 ''. _generate_extend 'X' 29 1 '>'. _generate_extend '.' 30 6 ''. _generate_extend 'X' 36 9 '<'. do_test( iv_line = lv_line iv_filename = '*.xml' ). ENDMETHOD. " test_xml_05 ENDCLASS. " ltcl_syntax_cases IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltcl_syntax_basic_logic DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltcl_syntax_basic_logic DEFINITION FINAL FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PRIVATE SECTION. DATA mo TYPE REF TO lcl_syntax_highlighter. METHODS: setup, process_line FOR TESTING, format_line FOR TESTING, apply_style FOR TESTING. ENDCLASS. " ltcl_syntax_basic_logic *----------------------------------------------------------------------* * CLASS ltcl_syntax_highlighter IMPLEMENTATION *----------------------------------------------------------------------* CLASS ltcl_syntax_basic_logic IMPLEMENTATION. METHOD setup. mo = lcl_syntax_highlighter=>create( '*.abap' ). ENDMETHOD. " setup METHOD format_line. DATA: lv_line TYPE string, lv_line_act TYPE string, lv_line_exp TYPE string. lv_line = 'call function ''FM_NAME''. " Commented'. "#EC NOTEXT lv_line_exp = 'call' && "#EC NOTEXT ' function' && "#EC NOTEXT ' 'FM_NAME'.' && "#EC NOTEXT ' " Commented'. "#EC NOTEXT lv_line_act = mo->process_line( iv_line = lv_line ). cl_abap_unit_assert=>assert_equals( exp = lv_line_exp act = lv_line_act msg = | Error during formating: { lv_line }| ). ENDMETHOD. " format_line METHOD apply_style. DATA lv_line_act TYPE string. " Call the method and compare results lv_line_act = mo->apply_style( iv_line = 'CALL FUNCTION' "#EC NOTEXT iv_class = lcl_syntax_abap=>c_css-keyword ). cl_abap_unit_assert=>assert_equals( act = lv_line_act exp = 'CALL FUNCTION' "#EC NOTEXT msg = 'Failure during applying of style.' ). "#EC NOTEXT ENDMETHOD. " apply_style METHOD process_line. DATA lv_line_act TYPE string. " Call the method with empty parameter and compare results lv_line_act = mo->process_line( iv_line = '' ). cl_abap_unit_assert=>assert_equals( act = lv_line_act exp = '' msg = 'Failure in method process_line.' ). "#EC NOTEXT " Call the method with non-empty line and compare results lv_line_act = mo->process_line( iv_line = '* CALL FUNCTION' ). "#EC NOTEXT cl_abap_unit_assert=>assert_equals( act = lv_line_act exp = '* CALL FUNCTION' "#EC NOTEXT msg = 'Failure in method process_line.' ). "#EC NOTEXT ENDMETHOD. " process_line ENDCLASS. " ltcl_syntax_highlighter