Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
---
Language: Cpp
AccessModifierOffset: -1
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: DontAlign
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
- Regex: '^<.*\.h>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 2
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 2000
PointerAlignment: Right
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
CanonicalDelimiter: ''
BasedOnStyle: google
ReflowComments: true
SortIncludes: false
SortUsingDeclarations: false
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Auto
TabWidth: 2
UseTab: Never
...
21 changes: 18 additions & 3 deletions .github/workflows/validate_clang_format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,32 @@ on:
- '**/*.h'
- '**/*.c'
- '**/*.cpp'
- '.clang-format'
workflow_dispatch:

permissions:
contents: read
contents: write # Required for auto-commit

jobs:
clang-format-checking:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: RafikFarhad/clang-format-github-action@v6
with:
sources: "**/*.h,**/*.c,**/*.cpp"
ref: ${{ github.head_ref }} # Required for auto-commit on PR branches

- name: Install clang-format
run: sudo apt-get install -y clang-format

- name: Apply clang-format
run: |
find ./components/nspanel_easy ./.test/unit \
\( -name "*.h" -o -name "*.c" -o -name "*.cpp" \) \
| xargs clang-format --style=file -i

- name: Commit clang-format fixes
uses: stefanzweifel/git-auto-commit-action@v6
with:
commit_message: "style: apply clang-format"
commit_options: "--no-verify"
...
11 changes: 11 additions & 0 deletions components/nspanel_easy/text.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,5 +178,16 @@ namespace nspanel_easy {
return code_point;
}

void replace_all(std::string& str, const char* token, const char* value) {
const size_t token_len = strlen(token);
if (token_len == 0) return; // Nothing to replace
const size_t value_len = strlen(value);
size_t pos = 0;
while ((pos = str.find(token, pos)) != std::string::npos) {
str.replace(pos, token_len, value);
pos += value_len; // Advance past the replacement to avoid infinite loop
}
}

} // namespace nspanel_easy
} // namespace esphome
13 changes: 13 additions & 0 deletions components/nspanel_easy/text.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,18 @@ namespace nspanel_easy {
return *a == *b;
}

/**
* @brief Replaces all occurrences of a token in a string with a given value.
*
* Iterates through the string replacing every occurrence of `token` with
* `value`. Safe for cases where `value` contains `token` as a substring,
* since the search position advances past each replacement.
*
* @param str The string to modify in place.
* @param token The substring to search for.
* @param value The replacement string.
*/
void replace_all(std::string& str, const char* token, const char* value);

} // namespace nspanel_easy
} // namespace esphome
40 changes: 10 additions & 30 deletions esphome/nspanel_esphome_datetime.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,38 +72,27 @@ script:
ESPTime now = id(time_provider).now();
std::string time_format_str = id(mui_time_format);

// Looped token replacer — handles repeated tokens in the same format string
auto replace_token = [](std::string& str, const char* token, const char* value) {
size_t pos = 0;
const size_t token_len = strlen(token);
const size_t value_len = strlen(value);
while ((pos = str.find(token, pos)) != std::string::npos) {
str.replace(pos, token_len, value);
pos += value_len;
}
};

// Resolve %-H (no-padding 24h hour)
replace_token(time_format_str, "%-H", to_string(static_cast<int>(now.hour)).c_str());
nspanel_easy::replace_all(time_format_str, "%-H", to_string(static_cast<int>(now.hour)).c_str());

// Resolve %-I (no-padding 12h hour)
const int hour12 = (now.hour == 0) ? 12 : (now.hour > 12) ? (now.hour - 12) : now.hour;
replace_token(time_format_str, "%-I", to_string(hour12).c_str());
nspanel_easy::replace_all(time_format_str, "%-I", to_string(hour12).c_str());

const char* const meridiem_text = (now.hour < 12) ?
"${LOCALIZATION[LANG].meridiem.am}" :
"${LOCALIZATION[LANG].meridiem.pm}";
"${LOCALIZATION[LANG].meridiem.am}" :
"${LOCALIZATION[LANG].meridiem.pm}";
const bool has_meridiem = (time_format_str.find("%p") != std::string::npos);

if (current_page->state == "screensaver" and id(screensaver_display_time)) {
std::string time_format_str_sleep = time_format_str;
replace_token(time_format_str_sleep, "%p", meridiem_text);
nspanel_easy::replace_all(time_format_str_sleep, "%p", meridiem_text);
disp1->set_component_text("text", now.strftime(time_format_str_sleep).c_str());
}

// Resolve %p for home page — strip from time string, set meridiem component separately
// Trim both ends after replacement to avoid leading/trailing whitespace
replace_token(time_format_str, "%p", "");
nspanel_easy::replace_all(time_format_str, "%p", "");
while (!time_format_str.empty() && time_format_str.front() == ' ')
time_format_str.erase(time_format_str.begin());
while (!time_format_str.empty() && time_format_str.back() == ' ')
Expand Down Expand Up @@ -167,19 +156,10 @@ script:
const int mon = dt.month - 1;

std::string date_str = id(mui_date_format);
auto replace_token = [](std::string& str, const char* token, const char* value) {
size_t pos = 0;
const size_t token_len = strlen(token);
const size_t value_len = strlen(value);
while ((pos = str.find(token, pos)) != std::string::npos) {
str.replace(pos, token_len, value);
pos += value_len;
}
};
replace_token(date_str, "%A", weekdays[dow]);
replace_token(date_str, "%a", weekdays_short[dow]);
replace_token(date_str, "%B", months[mon]);
replace_token(date_str, "%b", months_short[mon]);
nspanel_easy::replace_all(date_str, "%A", weekdays[dow]);
nspanel_easy::replace_all(date_str, "%a", weekdays_short[dow]);
nspanel_easy::replace_all(date_str, "%B", months[mon]);
nspanel_easy::replace_all(date_str, "%b", months_short[mon]);

disp1->set_component_text(component.c_str(), dt.strftime(date_str).c_str());

Expand Down
Loading