Edit von @biologist am 05.11.2023:
- Wir haben uns final für clang-format entschieden.
- Wie man das einrichtet ist weiter unten beschrieben.
Hallo,
Ich ziehe das Thema aus Zugangsdaten mehrerer Wlans speichern heraus. Hintergrund, ich habe einige Code Format Tools für VSCode (bzw für C und C++) angeschaut und wollte euch die mal vorstellen, bzw Inputs holen.
Bei der Auswahl habe ich mich in erster Linie auf Tools gestürzt, die ich selbst kenne.
Diese Punkte wären ein automatisches No-Go für ein Tool:
- Es muss von VSCode Native order per Plugin unterstützt sein, oder sich in Platformio / Github als Build integrieren lassen
- Es muss Binaries / Sources für die gängisten Betriebsysteme geben (Windows, Mac: Binary, Linux: min. Source)
- Die Konfiguration muss über ein Config-File im Projektordner erfolgen (können). Ohne dem müsste jeder alle Einstellungen manuell machen → No-Go
- Die Config-File muss Text-Basiert sein, damit Git es erkennen und aufnehmen kann.
Als Tool für die Erstellung der Beispiele weiter unten habe ich UniversalIndentGUI gefunden und damit herumgespielt gehabt.
C-Code
Original Code der für alle Formatter verwendet wurde
#include <Arduino.h>
#include "settings.h"
#include "Log.h"
#include "Button.h"
#include "Cmd.h"
#include "Port.h"
#include "System.h"
bool gButtonInitComplete = false;
// Only enable those buttons that are not disabled (99 or >115)
// 0 -> 39: GPIOs
// 100 -> 115: Port-expander
#if (NEXT_BUTTON >= 0 && NEXT_BUTTON <= MAX_GPIO)
#define BUTTON_0_ENABLE
#elif (NEXT_BUTTON >= 100 && NEXT_BUTTON <= 115)
#define EXPANDER_0_ENABLE
#endif
class z {
public:
z() { }
void doStuff(int z) { return l; }
};
t_button gButtons[7]; // next + prev + pplay + rotEnc + button4 + button5 + dummy-button
uint8_t gShutdownButton = 99; // Helper used for Neopixel: stores button-number of shutdown-button
uint16_t gLongPressTime = 0;
#ifdef PORT_EXPANDER_ENABLE
extern bool Port_AllowReadFromPortExpander;
#endif
static volatile SemaphoreHandle_t Button_TimerSemaphore;
void Button_Init() {
#if (WAKEUP_BUTTON >= 0 && WAKEUP_BUTTON <= MAX_GPIO)
if (ESP_ERR_INVALID_ARG == esp_sleep_enable_ext0_wakeup((gpio_num_t)WAKEUP_BUTTON, 0)) {
snprintf(Log_Buffer, Log_BufferLength, "%s (GPIO: %u)", (char *) FPSTR(wrongWakeUpGpio), WAKEUP_BUTTON);
Log_Println(Log_Buffer, LOGLEVEL_ERROR);
}
#endif
if (x) {
y = z;
}
switch (a) {
case b:
break;
}
for (int i = 0; i < 5; i++) {
i--;
}
#ifdef NEOPIXEL_ENABLE // Try to find button that is used for shutdown via longpress-action (only necessary for Neopixel)
#if defined(BUTTON_0_ENABLE) || defined(EXPANDER_0_ENABLE)
#if (BUTTON_0_LONG == CMD_SLEEPMODE)
gShutdownButton = 0;
#endif
#endif
#if defined(BUTTON_1_ENABLE) || defined(EXPANDER_1_ENABLE)
#if (BUTTON_1_LONG == CMD_SLEEPMODE)
gShutdownButton = 1;
#endif
#endif
#if defined(BUTTON_2_ENABLE) || defined(EXPANDER_2_ENABLE)
#if (BUTTON_2_LONG == CMD_SLEEPMODE)
gShutdownButton = 2;
#endif
#endif
#if defined(BUTTON_3_ENABLE) || defined(EXPANDER_3_ENABLE)
#if (BUTTON_3_LONG == CMD_SLEEPMODE)
gShutdownButton = 3;
#endif
#endif
#if defined(BUTTON_4_ENABLE) || defined(EXPANDER_4_ENABLE)
#if (BUTTON_4_LONG == CMD_SLEEPMODE)
gShutdownButton = 4;
#endif
#endif
#if defined(BUTTON_5_ENABLE) || defined(EXPANDER_5_ENABLE)
#if (BUTTON_5_LONG == CMD_SLEEPMODE)
gShutdownButton = 5;
#endif
#endif
#endif
}
Clang-Format
Teil vom LLVM und wohl der Klassiker unter den Code Format Tools. Hat viele Einstellungsmöglichkeiten und wird von VSCode (bzw dem C++ Plugin) nativ unterstützt. Großes Manko: Hasst den Präprozessor
.clang-format
---
BasedOnStyle: WebKit
AlignArrayOfStructures: Right
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: InlineOnly
BreakArrays: false
Cpp11BracedListStyle: true
FixNamespaceComments: true
IncludeBlocks: Regroup
IncludeCategories:
- Regex: Arduino.h
Priority: -1
CaseSensitive: true
SortPriority: -1
- Regex: ^"
Priority: 1
- Regex: .*
Priority: 4
IncludeIsMainRegex: (_test)?$
IndentCaseLabels: true
IndentPPDirectives: BeforeHash
IndentWidth: 8
InsertBraces: true
NamespaceIndentation: None
PointerAlignment: Right
SpaceAfterCStyleCast: true
UseTab: Always
Ergebnis
#include <Arduino.h>
#include "Button.h"
#include "Cmd.h"
#include "Log.h"
#include "Port.h"
#include "System.h"
#include "settings.h"
bool gButtonInitComplete = false;
// Only enable those buttons that are not disabled (99 or >115)
// 0 -> 39: GPIOs
// 100 -> 115: Port-expander
#if (NEXT_BUTTON >= 0 && NEXT_BUTTON <= MAX_GPIO)
#define BUTTON_0_ENABLE
#elif (NEXT_BUTTON >= 100 && NEXT_BUTTON <= 115)
#define EXPANDER_0_ENABLE
#endif
class z {
public:
z() { }
void doStuff(int z) { return l; }
};
t_button gButtons[7]; // next + prev + pplay + rotEnc + button4 + button5 + dummy-button
uint8_t gShutdownButton = 99; // Helper used for Neopixel: stores button-number of shutdown-button
uint16_t gLongPressTime = 0;
#ifdef PORT_EXPANDER_ENABLE
extern bool Port_AllowReadFromPortExpander;
#endif
static volatile SemaphoreHandle_t Button_TimerSemaphore;
void Button_Init()
{
#if (WAKEUP_BUTTON >= 0 && WAKEUP_BUTTON <= MAX_GPIO)
if (ESP_ERR_INVALID_ARG == esp_sleep_enable_ext0_wakeup((gpio_num_t) WAKEUP_BUTTON, 0)) {
snprintf(Log_Buffer, Log_BufferLength, "%s (GPIO: %u)", (char *) FPSTR(wrongWakeUpGpio), WAKEUP_BUTTON);
Log_Println(Log_Buffer, LOGLEVEL_ERROR);
}
#endif
if (x) {
y = z;
}
switch (a) {
case b:
break;
}
for (int i = 0; i < 5; i++) {
i--;
}
#ifdef NEOPIXEL_ENABLE // Try to find button that is used for shutdown via longpress-action (only necessary for Neopixel)
#if defined(BUTTON_0_ENABLE) || defined(EXPANDER_0_ENABLE)
#if (BUTTON_0_LONG == CMD_SLEEPMODE)
gShutdownButton = 0;
#endif
#endif
#if defined(BUTTON_1_ENABLE) || defined(EXPANDER_1_ENABLE)
#if (BUTTON_1_LONG == CMD_SLEEPMODE)
gShutdownButton = 1;
#endif
#endif
#if defined(BUTTON_2_ENABLE) || defined(EXPANDER_2_ENABLE)
#if (BUTTON_2_LONG == CMD_SLEEPMODE)
gShutdownButton = 2;
#endif
#endif
#if defined(BUTTON_3_ENABLE) || defined(EXPANDER_3_ENABLE)
#if (BUTTON_3_LONG == CMD_SLEEPMODE)
gShutdownButton = 3;
#endif
#endif
#if defined(BUTTON_4_ENABLE) || defined(EXPANDER_4_ENABLE)
#if (BUTTON_4_LONG == CMD_SLEEPMODE)
gShutdownButton = 4;
#endif
#endif
#if defined(BUTTON_5_ENABLE) || defined(EXPANDER_5_ENABLE)
#if (BUTTON_5_LONG == CMD_SLEEPMODE)
gShutdownButton = 5;
#endif
#endif
#endif
}
Vorteile
Clang kann gut mit dem c und c++ Syntax umgehen. Es hat sehr viele Konfigurationsmöglichkeiten und (wahrscheinlich der größte Plus-Punkt), es ist von Haus aus in VSCode verfügbar. Auch ist es recht einfach es in Github Actions einzubinden (buut, I’ve never done that).
Nachteile
Clang-Format geht (wie auch die anderen Formatter) sehr stiefmütterlich mit Makros um. Es ist schon schwer ihn zu überreden diese einzurücken, aber folgenden Style geht einfach nicht:
void x() {
#if x
y = 0;
#else
y = 1;
#endif
}
AStyle
Abürzung für Artistic Style. Hatte bis vor paar Wochen nichts von ihm gehört, aber es scheint relativ verbreitet zu sein und wird auch aktiv gepfelgt. Eine Unterstützung in VSCode muss sowohl Asytle (das Kommandozeilen Tool) als auch als Plugin Astyle installiert und konfiguriert werden.
.astylerc
-A2
--indent=tab
--indent-switches
--attach-closing-while
--indent-modifiers
--indent-preproc-cond
--indent-preproc-block
--indent-col1-comments
--min-conditional-indent=2
--pad-comma
--align-pointer=name
--align-reference=name
--break-one-line-headers
--add-braces
--attach-return-type-decl
--max-code-length=220
Ergebnis
#include <Arduino.h>
#include "settings.h"
#include "Log.h"
#include "Button.h"
#include "Cmd.h"
#include "Port.h"
#include "System.h"
bool gButtonInitComplete = false;
// Only enable those buttons that are not disabled (99 or >115)
// 0 -> 39: GPIOs
// 100 -> 115: Port-expander
#if (NEXT_BUTTON >= 0 && NEXT_BUTTON <= MAX_GPIO)
#define BUTTON_0_ENABLE
#elif (NEXT_BUTTON >= 100 && NEXT_BUTTON <= 115)
#define EXPANDER_0_ENABLE
#endif
class z {
public:
z() { }
void doStuff(int z) {
return l;
}
};
t_button gButtons[7]; // next + prev + pplay + rotEnc + button4 + button5 + dummy-button
uint8_t gShutdownButton = 99; // Helper used for Neopixel: stores button-number of shutdown-button
uint16_t gLongPressTime = 0;
#ifdef PORT_EXPANDER_ENABLE
extern bool Port_AllowReadFromPortExpander;
#endif
static volatile SemaphoreHandle_t Button_TimerSemaphore;
void Button_Init() {
#if (WAKEUP_BUTTON >= 0 && WAKEUP_BUTTON <= MAX_GPIO)
if (ESP_ERR_INVALID_ARG == esp_sleep_enable_ext0_wakeup((gpio_num_t)WAKEUP_BUTTON, 0)) {
snprintf(Log_Buffer, Log_BufferLength, "%s (GPIO: %u)", (char *) FPSTR(wrongWakeUpGpio), WAKEUP_BUTTON);
Log_Println(Log_Buffer, LOGLEVEL_ERROR);
}
#endif
if (x) {
y = z;
}
switch (a) {
case b:
break;
}
for (int i = 0; i < 5; i++) {
i--;
}
#ifdef NEOPIXEL_ENABLE // Try to find button that is used for shutdown via longpress-action (only necessary for Neopixel)
#if defined(BUTTON_0_ENABLE) || defined(EXPANDER_0_ENABLE)
#if (BUTTON_0_LONG == CMD_SLEEPMODE)
gShutdownButton = 0;
#endif
#endif
#if defined(BUTTON_1_ENABLE) || defined(EXPANDER_1_ENABLE)
#if (BUTTON_1_LONG == CMD_SLEEPMODE)
gShutdownButton = 1;
#endif
#endif
#if defined(BUTTON_2_ENABLE) || defined(EXPANDER_2_ENABLE)
#if (BUTTON_2_LONG == CMD_SLEEPMODE)
gShutdownButton = 2;
#endif
#endif
#if defined(BUTTON_3_ENABLE) || defined(EXPANDER_3_ENABLE)
#if (BUTTON_3_LONG == CMD_SLEEPMODE)
gShutdownButton = 3;
#endif
#endif
#if defined(BUTTON_4_ENABLE) || defined(EXPANDER_4_ENABLE)
#if (BUTTON_4_LONG == CMD_SLEEPMODE)
gShutdownButton = 4;
#endif
#endif
#if defined(BUTTON_5_ENABLE) || defined(EXPANDER_5_ENABLE)
#if (BUTTON_5_LONG == CMD_SLEEPMODE)
gShutdownButton = 5;
#endif
#endif
#endif
}
Vorteile
Der Formatter kann Preprozessor direktiven (halwegs) formatieren.
Nachteile
Das Tool muss manuell installiert und das VSCode Plugin eingestellt werden. Das war bei mir nicht am Einfachsten, aber wahrscheinlich mit einem kleinen How-To machbar.
Der Formatter hat gefühlt einige Bugs, wie zB wenn Abstände zwischen mathematischen Operatoren gewünscht sind, werden auch Pointer so formatiert (also zb aus Pointer *p
wird Pointer * p
).
Uncrustify
„Gefunden“ durch das UniversalIntendGUI. Sehr viele Einstellungsmöglichkeiten und vielleicht auch ein Volltreffer.
uncrustify.cfg
tok_split_gte=false
utf8_byte=false
utf8_force=false
indent_cmt_with_tabs=true
indent_align_string=false
indent_braces=false
indent_braces_no_func=false
indent_braces_no_class=false
indent_braces_no_struct=false
indent_brace_parent=false
indent_namespace=false
indent_extern=false
indent_class=false
indent_class_colon=false
indent_else_if=false
indent_var_def_cont=false
indent_func_call_param=false
indent_func_def_param=false
indent_func_proto_param=false
indent_func_class_param=false
indent_func_ctor_var_param=false
indent_template_param=false
indent_func_param_double=false
indent_relative_single_line_comments=false
indent_col1_comment=false
indent_access_spec_body=false
indent_paren_nl=false
indent_comma_paren=false
indent_bool_paren=false
indent_first_bool_expr=false
indent_square_nl=false
indent_preserve_sql=false
indent_align_assign=true
sp_balance_nested_parens=false
align_keep_tabs=false
align_with_tabs=false
align_on_tabstop=false
align_number_left=false
align_func_params=false
align_same_func_call_params=false
align_var_def_colon=false
align_var_def_attribute=false
align_var_def_inline=false
align_right_cmt_mix=false
align_on_operator=false
align_mix_var_proto=false
align_single_line_func=false
align_single_line_brace=false
align_nl_cont=false
align_left_shift=true
align_oc_decl_colon=false
nl_collapse_empty_body=false
nl_assign_leave_one_liners=false
nl_class_leave_one_liners=false
nl_enum_leave_one_liners=false
nl_getset_leave_one_liners=false
nl_func_leave_one_liners=false
nl_if_leave_one_liners=false
nl_multi_line_cond=false
nl_multi_line_define=false
nl_before_case=false
nl_after_case=false
nl_after_return=false
nl_after_semicolon=false
nl_after_brace_open=false
nl_after_brace_open_cmt=false
nl_after_vbrace_open=false
nl_after_vbrace_open_empty=false
nl_after_brace_close=false
nl_after_vbrace_close=false
nl_define_macro=false
nl_squeeze_ifdef=false
nl_ds_struct_enum_cmt=false
nl_ds_struct_enum_close_brace=false
nl_create_if_one_liner=false
nl_create_for_one_liner=false
nl_create_while_one_liner=false
ls_for_split_full=false
ls_func_split_full=false
nl_after_multiline_comment=false
eat_blanks_after_open_brace=false
eat_blanks_before_close_brace=false
mod_full_brace_if_chain=false
mod_pawn_semicolon=false
mod_full_paren_if_bool=false
mod_remove_extra_semicolon=false
mod_sort_import=false
mod_sort_using=false
mod_sort_include=false
mod_move_case_break=false
mod_remove_empty_return=false
cmt_indent_multi=true
cmt_c_group=false
cmt_c_nl_start=false
cmt_c_nl_end=false
cmt_cpp_group=false
cmt_cpp_nl_start=false
cmt_cpp_nl_end=false
cmt_cpp_to_c=false
cmt_star_cont=false
cmt_multi_check_last=true
cmt_insert_before_preproc=false
pp_indent_at_level=false
pp_region_indent_code=false
pp_if_indent_code=true
pp_define_at_level=true
indent_switch_case=4
newlines=crlf
indent_with_tabs=2
sp_arith=add
sp_assign=add
sp_assign_default=add
mod_full_brace_do=force
mod_full_brace_for=force
mod_full_brace_function=force
mod_full_brace_if=force
mod_full_brace_while=force
Ergebnis
#include <Arduino.h>
#include "settings.h"
#include "Log.h"
#include "Button.h"
#include "Cmd.h"
#include "Port.h"
#include "System.h"
bool gButtonInitComplete = false;
// Only enable those buttons that are not disabled (99 or >115)
// 0 -> 39: GPIOs
// 100 -> 115: Port-expander
#if (NEXT_BUTTON >= 0 && NEXT_BUTTON <= MAX_GPIO)
#define BUTTON_0_ENABLE
#elif (NEXT_BUTTON >= 100 && NEXT_BUTTON <= 115)
#define EXPANDER_0_ENABLE
#endif
class z {
public:
z() { }
void doStuff(int z) { return l; }
};
t_button gButtons[7]; // next + prev + pplay + rotEnc + button4 + button5 + dummy-button
uint8_t gShutdownButton = 99; // Helper used for Neopixel: stores button-number of shutdown-button
uint16_t gLongPressTime = 0;
#ifdef PORT_EXPANDER_ENABLE
extern bool Port_AllowReadFromPortExpander;
#endif
static volatile SemaphoreHandle_t Button_TimerSemaphore;
void Button_Init() {
#if (WAKEUP_BUTTON >= 0 && WAKEUP_BUTTON <= MAX_GPIO)
if (ESP_ERR_INVALID_ARG == esp_sleep_enable_ext0_wakeup((gpio_num_t)WAKEUP_BUTTON, 0)) {
snprintf(Log_Buffer, Log_BufferLength, "%s (GPIO: %u)", (char *) FPSTR(wrongWakeUpGpio), WAKEUP_BUTTON);
Log_Println(Log_Buffer, LOGLEVEL_ERROR);
}
#endif
if (x) {
y = z;
}
switch (a) {
case b:
break;
}
for (int i = 0; i < 5; i++) {
i--;
}
#ifdef NEOPIXEL_ENABLE // Try to find button that is used for shutdown via longpress-action (only necessary for Neopixel)
#if defined(BUTTON_0_ENABLE) || defined(EXPANDER_0_ENABLE)
#if (BUTTON_0_LONG == CMD_SLEEPMODE)
gShutdownButton = 0;
#endif
#endif
#if defined(BUTTON_1_ENABLE) || defined(EXPANDER_1_ENABLE)
#if (BUTTON_1_LONG == CMD_SLEEPMODE)
gShutdownButton = 1;
#endif
#endif
#if defined(BUTTON_2_ENABLE) || defined(EXPANDER_2_ENABLE)
#if (BUTTON_2_LONG == CMD_SLEEPMODE)
gShutdownButton = 2;
#endif
#endif
#if defined(BUTTON_3_ENABLE) || defined(EXPANDER_3_ENABLE)
#if (BUTTON_3_LONG == CMD_SLEEPMODE)
gShutdownButton = 3;
#endif
#endif
#if defined(BUTTON_4_ENABLE) || defined(EXPANDER_4_ENABLE)
#if (BUTTON_4_LONG == CMD_SLEEPMODE)
gShutdownButton = 4;
#endif
#endif
#if defined(BUTTON_5_ENABLE) || defined(EXPANDER_5_ENABLE)
#if (BUTTON_5_LONG == CMD_SLEEPMODE)
gShutdownButton = 5;
#endif
#endif
#endif
}
Vorteile
Es gibt ein VSCode Plugin Uncrustify, den ich aber noch nicht ausgetestet habe. Das Tool hat sehr viele Einstellungsmöglichkeiten und wird aktiv entwickelt. Es gibt auch Github Actions um commits gegen eine Konfiguration zu testen.
Nachteile
Der Formatter muss extern instelliert werden, es gibt aber Builds für Linux und Windows (somit ist das kein echter Nachteil sondern eher ein „eh“). Die Masse an Einstellungen ist wortwörtlich erschlagend und ohne der GUI wäre es noch einmal schlimmer da die Parameter nicht selbsterklärend sind.
Builtin VSCode Formatter
Benutzt Clang, wenn die Datei .clang-format
nicht gefunden wird, wird das default Style „Visual Studio“ verwendet. Das entspricht der folgenden Config:
UseTab: (VS Code current setting)
IndentWidth: (VS Code current setting)
BreakBeforeBraces: Allman
AllowShortIfStatementsOnASingleLine: false
IndentCaseLabels: false
ColumnLimit: 0
Damit habe ich das nicht weiter betrachtet.
HTML & Javascript
Prettier
Meiner Meinung nach eines der besten Formatter für Javascript und HTML. Kann mit fast allem was Web im Namen hat umgehen, HTML, Javascript, TypeScript und JSX werden bei uns wohl die wichtigsten sein (eine ausführliche Liste ist in der Doku zu finden). Es hat sehr viele Einstellungsmöglichkeiten und kann auch durch ein Config-File gesteuert werden.
TL;DR
Das sind meine gefunden Tools. Aktuell liebäuge ich mit Uncrustify, aber ich bin für Ideen offen, wenn ihr noch andere Tools für die Formatierung kennt. Was halt sehr angenehm wäre, wenn wir zu 100% Toolgestützt sind. Dann muss keine Zeit verbraten werden nur um die Formatierung richtig zu ziehen (ich erwische mich immer, dass ich reflexartig SHIFT+ALT+F drücke und die Formatierung zerstöre )