diumenge, de desembre 09, 2012

Fix formatting of ABAP code in correction instructions

ABAP code snippets in correction instructions are difficult to read, mostly because they don't use any syntax highlighting at all:



 I usually copy the code into vim and use the ready-to-use ABAP syntax highlighting (recently I started to do it with Text Editor Anywhere, so that the file type is automatically set).  The problem with this is that the copied code is formatted in such a way that breaks the syntax highlighting.

As illustrated in the picture below, there are typically two issues:

1) All lines are left-padded with two spaces (green bar on the left). As a consequence, comment lines signalized by a '*' in the first column are not longer recognized as such.
2) Lines longer than 74 characters are broken into two. If the line is a comment, the later part (which goes in the second line) is not preceded by a comment mark '*' and the resulting syntax highlighting is wrong again.

Example:


The following commands aim at solving both problems.  Kind of.

1) In normal mode execute:

go^QlG0llx

(^Q denotes Control-Q; you have to enter ^Q twice to get a single literal ^Q)

go places the cursor on the topmost line, leftmost position
Ctrl-Q enables block mode
G extends the block selection up to the last line
0 sets the right border of the selection at column 0
l shifts the selection one character to the left; ll shifts the selection two columns to the left
x deletes the selected block

2) In command mode execute:
g/^\%(\.\.\.\)\@![^* ]/-1 join

^\%(\.\.\.\) matches '...' at the beginning; \%(\) is a non-capturing group.
[^* ] matches every character different from '*' or space.
\@! is a zero-with match whenever the preceding atom does not match.
 ^\%(\.\.\.\)\@![^* ] is any character not '*' or space at the beginning, provided that is not the starting point of a '...'
g//-1 points to the preceding line.

The exclusion of '...' is necessary because SAP inserts these lines into the code to indicate that the code has been snipped off at that position.

After this small surgery the ABAP syntax highlighting is correct and the long comment lines are contained in a single line:



 Still there are some border cases that escape the above incantation, but this is a 80-20 kind of thing.

Update 09.12.2012:

If you are using Text Editor Anywhere, you can automate the whole thing a bit further by putting the following function into your _vimrc file:

function! Fix_ABAP_CI()
        normal go
^QG0llx
        g/^\%(\.\.\.\)\@![^* ]/-1 join!
        setlocal number nowrap
endfunction


and invoking it from the TEA trigger itself as follows:

Extension: abap
Editor: c:\
Parameters: -c "call Fix_ABAP_CI()" "%1"




Even more elegant is to set up the TEA trigger with a custom extension .abap_ci and define a vim autocommand to set the ABAP filetype based on the extension to fire the above function.  The filetype can be also handled inside the function:


Edit $HOME/vimfiles/filetype.vim and add the following:


augroup filetypedetect
  au! BufRead,StdinReadPost *.abap_ci call Fix_ABAP_CI()
augroup END

function! Fix_ABAP_CI()
        setfiletype abap
        normal go
^QG0llx
        g/^\%(\.\.\.\)\@![^* ]/-1 join!
        setlocal number nowrap
endfunction


The TEA trigger looks like this:




Update 14.12.2013

To better highlight insertions and deletions, you can make use of the 'signs' features (:help signs).
Enhance the Fix_ABAP_CI in this way:

function! Fix_ABAP_CI()

        setfiletype abap


        " delete left padding"
        normal go G0llx


        " join lines that have been broken at the 74th column
        " These are those starting at column 0

        " except comments and elipsis"
        g/^\%(\.\.\.\)\@![^* ]/-1 join!


        setlocal number nowrap
        " define signs for insertion and deletion
        " Note: The highliting groups are ok for
        "       jellybeans similar dark themes
        "       but they dont look always great in other themes.
        sign define CIDelete text=<< texthl=WarningMsg
        sign define CIInsert text=>> texthl=Title

        " Put the command for defining the sign into a register
        " We need this since :exe does not support ranges
        let @q=':sign place ^R =line(".")^M name=CIInsert line=^R =line(".")^M file= ^R=expand("%:p")^M^M'
         
        let @r=':sign place ^R=line(".")^M name=CIDelete line= ^R=line(".")^M file= ^R=expand("%:p")^M^M'

        " begin from top of the file
        normal go

        " define the signs on the relevant lines
        g~START OF INSERTION~ +1;/END OF INSERTION/-1 normal @q
        g~START OF DELETION~  +1;/END OF DELETION/-1  normal @r
endfunction