ABAP Tools - Plugin ABAP Cleaner
Kein Bock mehr auf Code formatieren und dein Kollege macht es eh immer komplett anders? Heute stellen wir das Plugin ABAP Cleaner vor, welches eine Menge Arbeit abnimmt.
Inhaltsverzeichnis
Bei den ABAP Development Days letzte Woche hat SAP endlich die Katze aus dem Sack gelassen und das neue Feature für die ADTs freigegeben für die öffentliche Nutzung. Der ABAP Cleaner ist ein Plugin für Eclipse, womit du deinen Code einheitlich aufräumen kannst. Du kannst die Einstellungen aber auch an deine Kollegen verteilen, sodass in Zukunft im gesamten Team der gleich formatierte Code erzeugt wird und ihr auf einem Stand seid.
Einleitung
Doch fangen wir vielleicht noch einmal kurz von vorn an. Seit dem ABAP Code entwickelt wird, gibt es unterschiedliche Formen und Best Practices wie eigentlich ABAP Quellcode formatiert wird. In der Vergangenheit wurde dafür auch in den meisten Fällen ein einheitlicher Pretty Printer verwendet, doch dieser hatte bereits seine Grenzen erreicht, wenn es um SELECT Statements ging, wie Forms oder Methodenaufrufe definiert werden.
Neben dem schönen Code kamen die letzten Jahre auch viele neue Statements heraus, die das Modern ABAP einläuteten. Die Statements können mittlerweile schon recht lang werden und können noch dazu auf x-weisen formatiert werden. In diesem Zuge wurde auch der Guide für Clean ABAP veröffentlicht, um wieder etwas Ordnung und Best Practice in den Code zu bringen. Im nächsten Schritt hat SAP an einer Erweiterung des ABAP Test Cockpit (ATC) gearbeitet, dem "Code pal", damit diese Regeln auch programmatisch geprüft werden können.
ABAP Cleaner
Aus diesen ganzen Vorgaben, plus jede Menge altem Code entstand daraus nun der ABAP Cleaner, ein Plugin für Eclipse, was zusammen mit den ABAP Development Tools (ADT) genutzt werden kann. Der ABAP Cleaner formatiert, ändert und modernisiert den ABAP Code mit einer Tastenkombination. Das Projekt existiert als Open Source und zusammen mit der ABAP Community möchte SAP das Tool ausbauen und neue Funktionen zur Verfügung stellen. Über das Git Repository können Issues aufgemacht werden oder direkt mit Pull requests unterstützt werden.
Umfang
Doch was steckt nun eigentlich alles an Funktionen in dem Tool? Dazu gibt es in dem Repository eine genaue Auflistung der aktuellen Funktionen, die wir dir hier einmal zusammenfassen wollen:
- Formatierung - Leerzeichen oder Zeilen anpassen, komplexe VALUE Statements ausrichten, Methodenaufrufe formatieren, Zuweisungen ausrichten. Hier wird der Großteil des ABAP Codes entsprechend dem Styleguide einheitlich angepasst und muss nicht mehr manuell formatiert werden.
- Refactoring - Die Refactoring Funktion des Plugins ist sehr stark, so können viele alte Statements automatisch ersetzt werden, wie die Vergleichsoperatoren, Entfernung der unnötigen Selbstreferenz ME, Aufruf von Methoden auf die funktionale Schreibweise.
- Logikanpassung - Gehört eigentlich zum Teil des Refactoring, wollten wir hier aber noch einmal extra erwähnen. So werden zu zu lange IF Statements angeschaut und die CHECKs teilweise konvertiert nach den neuen Regeln.
Die Hauptkategorien bei der Beschreibung sind: Leere Zeilen, Leerzeichen, Deklarationen, Syntax, Commands, Pretty Printer und Ausrichtung.
Installation
Für die Installation wird eine aktuelle Version von Eclipse mit installieren ABAP Development Tools (ADT) benötigt. Dazu Eclipse starten und im Menü über "Help -> Install New Software..." die aktuelle Updatesite "https://sap.github.io/abap-cleaner/updatesite" eintragen. Diese findest du auch in der Dokumentation des Repositories:
Dann nur noch bestätigen, die Nutzungsbedingung und der Lizenz zustimmen und schon wird die Installation durchgeführt. Am Ende muss Eclipse noch einmal neu gestartet werden, damit das Plugin aktiv wird.
Nutzung
Das Tool kann in aktivem Quellcode über das Menü "Source Code" aufgerufen werden. Hier stehen die beiden Optionen "Automatikmodus" oder "Interaktivmodus" zur Verfügung.
Alternativ und später kann die Funktion dann mit den Tastenkombinationen ausgelöst werden, hierfür stehen im Standard STRG + 4 und STRG + SHIFT + 4 zur Verfügung. Der Automatikmodus wendet das aktuell aktive Profil direkt auf dem markierten Quellcode aus und führt die Formatierung aus. Im Interaktivmodus hast du die Möglichkeit alle Änderungen zu prüfen und zu schauen, was verändert wird.
Beispiel
Nehmen wir dazu einmal etwas Beispielcode, den wir extra als Demo für die Formatierung vorbereitet haben. Dieser Code ist funktionstüchtig, allerdings sehr schlecht formatiert. Anhand dieses Beispiels wollen wir einmal die Features erklären.
CLASS zcl_bs_demo_abap_cleaner DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun .
PROTECTED SECTION.
PRIVATE SECTION.
types:
td_char type c LENGTH 3,
"! Structure for data
begin of ts_data,
some_id type td_char,
some_text type string,
a_number type i,
end of ts_data,
"! Table for data
tt_data type STANDARD TABLE OF ts_data with DEFAULT KEY.
data:
mt_data type tt_data,
md_configuration type abap_bool.
methods:
"! Load intial data into table
"! @parameter rt_result | Loaded data
get_inital_data RETURNING VALUE(rt_result) type tt_data,
"! Output data into console
"! @parameter io_out | Reference for console
"! @parameter is_data | Data to be printed
output_something IMPORTING
io_out type ref to if_oo_adt_classrun_out
is_data type ts_data.
ENDCLASS.
CLASS zcl_bs_demo_abap_cleaner IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
data(lt_data) = me->get_inital_data( ).
loop at lt_data INTO data(ls_data).
check: ls_data-a_number GE 5.
output_something( EXPORTING io_out = out
is_data = ls_data
).
ENDLOOP.
ENDMETHOD.
METHOD get_inital_data.
rt_result =
value #(
( some_id = 'A' some_text = 'ABC' a_number = 2 )
( some_id = 'B' some_text = 'ABC' a_number = 10 )
( some_id = 'C' some_text = 'DEF' a_number = 8 )
( some_id = 'A' some_text = 'GIH' a_number = 7 )
).
ENDMETHOD.
METHOD output_something.
call method io_out->write
EXPORTING
data = |ID: { is_data-some_id } with Text: { is_data-some_text }|
."method call
ENDMETHOD.
ENDCLASS.
Pretty Printer
Bereits der Pretty Printer kann hier einiges machen und sollte bereits fest in deiner Entwicklung verankert sein. Doch dieser macht nur einige Einrückungen, die Methoden sehen weiterhin schief aus.
CLASS zcl_bs_demo_abap_cleaner DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun .
PROTECTED SECTION.
PRIVATE SECTION.
TYPES:
td_char TYPE c LENGTH 3,
"! Structure for data
BEGIN OF ts_data,
some_id TYPE td_char,
some_text TYPE string,
a_number TYPE i,
END OF ts_data,
"! Table for data
tt_data TYPE STANDARD TABLE OF ts_data WITH DEFAULT KEY.
DATA:
mt_data TYPE tt_data,
md_configuration TYPE abap_bool.
METHODS:
"! Load intial data into table
"! @parameter rt_result | Loaded data
get_inital_data RETURNING VALUE(rt_result) TYPE tt_data,
"! Output data into console
"! @parameter io_out | Reference for console
"! @parameter is_data | Data to be printed
output_something IMPORTING
io_out TYPE REF TO if_oo_adt_classrun_out
is_data TYPE ts_data.
ENDCLASS.
CLASS zcl_bs_demo_abap_cleaner IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA(lt_data) = me->get_inital_data( ).
LOOP AT lt_data INTO DATA(ls_data).
CHECK: ls_data-a_number GE 5.
output_something( EXPORTING io_out = out
is_data = ls_data
).
ENDLOOP.
ENDMETHOD.
METHOD get_inital_data.
rt_result =
VALUE #(
( some_id = 'A' some_text = 'ABC' a_number = 2 )
( some_id = 'B' some_text = 'ABC' a_number = 10 )
( some_id = 'C' some_text = 'DEF' a_number = 8 )
( some_id = 'A' some_text = 'GIH' a_number = 7 )
).
ENDMETHOD.
METHOD output_something.
CALL METHOD io_out->write
EXPORTING
data = |ID: { is_data-some_id } with Text: { is_data-some_text }|
. "method call
ENDMETHOD.
ENDCLASS.
Interaktivmodus
Markieren wir nun den ganzen Quelltext und starten den Interaktivmodus mit STRG + SHIFT + 4, dann erhalten wir ein Fenster mit neuen Möglichkeiten, um eine Vorschau der Änderungen zu bekommen.
Was sehen wir eigentlich nun im Fenster? Im Quellcode werden die typischen Farben für den Versionsvergleich verwendet. Rot sind entfernte Zeilen, Grün sind neue Zeilen, Gelb wurde geändert und graue Zeilen existieren in diesem Codeabschnitt nicht. Hier eine kurze Erklärung der Funktionen:
- Linke Seite - Original Quellcode ohne Anpassungen
- Rechte Seite - Neuer Quellcode nach der Anpassung
- Toolleiste
- Profile - Verschiedene Profile zum Formatieren des Quellcodes, Einstellung der Profile und Angabe des aktuellen Releases des Quellcodes.
- Display - Damit können bestimmte Änderungen am Quellcode farblich markiert oder ausgeblendet werden.
- Sucheinstellung - Für die Suche im Quellcode können weitere Einstellungen vorgenommen werden.
- Genutzte Regeln - Es können Zeilen oder ganze Abschnitte im Vergleich markiert werden, für diesen Abschnitt werden alle aktiven Regeln angezeigt und du kannst eigene Regeln zum Testen deaktivieren.
Vergleich
Schauen wir uns einmal einen Code-Block an und prüfen die durchgeführten Änderungen. Dazu nehmen wir im Beispiel den Loop und schauen uns zuerst einmal den Vergleich der Abschnitte an:
Das Check Statement ist verschwunden und wurde gegen eine IF-CONTINUE Variante ausgetauscht, umso besser für Entwickler lesbar zu sein. Der Vergleichsoperator wurde gegen die allgemeine Option ausgetauscht und ist nun ebenfalls einfacher lesbar. Beim Aufruf der Methode wurde das Exporting entfernt, die Parameter wurden im Aufruf formatiert und die schließende Klammer befindet sich nun auf der letzten Zeile. Wenn wir den Code-Block markieren, bekommen wir insgesamt 7 verschiedene Änderungen angezeigt:
Ergebnis
Hier noch einmal das finale Ergebnis der Klasse nach Durchführung der Funktion im "default" Profil.
CLASS zcl_bs_demo_abap_cleaner DEFINITION
PUBLIC
FINAL
CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PRIVATE SECTION.
TYPES:
td_char TYPE c LENGTH 3,
"! Structure for data
BEGIN OF ts_data,
some_id TYPE td_char,
some_text TYPE string,
a_number TYPE i,
END OF ts_data,
"! Table for data
tt_data TYPE STANDARD TABLE OF ts_data WITH DEFAULT KEY.
DATA mt_data TYPE tt_data.
DATA md_configuration TYPE abap_bool.
"! Load initial data into table
"! @parameter rt_result | Loaded data
METHODS get_inital_data RETURNING VALUE(rt_result) TYPE tt_data.
"! Output data into console
"! @parameter io_out | Reference for console
"! @parameter is_data | Data to be printed
METHODS output_something IMPORTING io_out TYPE REF TO if_oo_adt_classrun_out
is_data TYPE ts_data.
ENDCLASS.
CLASS zcl_bs_demo_abap_cleaner IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA(lt_data) = get_inital_data( ).
LOOP AT lt_data INTO DATA(ls_data).
IF ls_data-a_number < 5.
CONTINUE.
ENDIF.
output_something( io_out = out
is_data = ls_data ).
ENDLOOP.
ENDMETHOD.
METHOD get_inital_data.
rt_result =
VALUE #( ( some_id = 'A' some_text = 'ABC' a_number = 2 )
( some_id = 'B' some_text = 'ABC' a_number = 10 )
( some_id = 'C' some_text = 'DEF' a_number = 8 )
( some_id = 'A' some_text = 'GIH' a_number = 7 ) ).
ENDMETHOD.
METHOD output_something.
io_out->write( data = |ID: { is_data-some_id } with Text: { is_data-some_text }| ). " method call
ENDMETHOD.
ENDCLASS.
Konfiguration
Die Konfiguration der Profile erreicht man über den Interaktivmodus (STRG + SHIFT + 4) und den Button "Edit Profiles and Rules...". Hier kannst du neue Profile erstellen und die Einstellungen anpassen:
Das Bild ist wie folgt aufgebaut:
- Oben Links - Verwaltung der Profile
- Open Rechts - Einstellungen zur gewählten Regel
- Unten Links - Verfügbare Regeln und weitere Optionen
- Unten Rechts - Code Beispiele zur ausgewählten Regel, um die Einstellungen zu sehen
Hinweis: Um deinen Kollegen die gleiche Konfiguration mitzugeben, falls sie von "default" abweicht, kannst du die über Export und Import machen. So könnt ihr die Datei zentral ablegen und austauschen.
Systemstand
Einige der Regeln arbeiten auf älteren Systemen nicht, wenn es zum Beispiel um neue Statements geht. Hier solltest du mit dem Interaktivmodus arbeiten, dort kannst du die Version des Codes einstellen. Dann werden bestimmte Regeln nicht mehr ausgeführt und nur korrekte Änderungen durchgeführt:
Fazit
Das neue Tool kann in der heutigen Zeit unheimlich viel Zeit ersparen und für einen einheitlichen Stand im Entwicklerteam sorgen. Gleich formatierter Quellcode ist von allen gleich gut lesbar und reduziert den Einarbeitungsaufwand in neues Coding, da gewisse Standards gesetzt sind. Gleichzeitig ermöglichen die Refactoring Optionen alten Quellcode zu modernisieren und unnötigen Quellcode zu vermeiden.