Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
90 changes: 86 additions & 4 deletions doc/contribute/guidelines.rst
Original file line number Diff line number Diff line change
Expand Up @@ -597,10 +597,10 @@ Running CI Tests Locally
check_compliance.py
-------------------

The ``check_compliance.py`` script serves as a valuable tool for assessing code
compliance with Zephyr's established guidelines and best practices. The script
acts as wrapper for a suite of tools that performs various checks, including
linters and formatters.
The :zephyr_file:`scripts/ci/check_compliance.py` script serves as a valuable
tool for assessing code compliance with Zephyr's established guidelines and
best practices. The script acts as wrapper for a suite of tools that performs
various checks, including linters and formatters.

Developers are encouraged to run the script locally to validate their changes
before opening a new Pull Request:
Expand All @@ -616,6 +616,88 @@ before opening a new Pull Request:
Set the default app to Strawberry Perl. By default the executable is
installed at ``C:\Strawberry\perl\bin\perl.exe``.

KeepSorted Check
^^^^^^^^^^^^^^^^

The KeepSorted check ensures that specified blocks of code, configuration, or
documentation remain in sorted order.

To use the KeepSorted check, wrap your sorted content between dedicated lines
containing start and stop markers, typically using comments:

.. code-block:: c

// zephyr-keep-sorted-start
option_a
option_b
option_c
// zephyr-keep-sorted-stop

KeepSorted Marker Options
"""""""""""""""""""""""""

The sorting behavior of each block can be customized in several ways.
To do this, one or more of the following parameters can be added on
the same line as the start marker itself:

**re(regex_pattern)**
Enables regex mode where only lines matching the specified regular expression
are checked for sorting. Other lines are ignored.

Example checking sorted properties in a yaml file:

.. code-block:: yaml

# zephyr-keep-sorted-start re(^\s+\- name:)
projects:
- name: application
revision: main
- name: library1
revision: feature-branch
- name: library2
revision: main
# zephyr-keep-sorted-stop

**strip(characters)**
Strips the specified characters from lines before performing the sort comparison.
This is useful when lines have optional prefixes or suffixes that should be
ignored during sorting.

Example stripping quotes from yaml dictionary keys:

.. code-block:: yaml

# zephyr-keep-sorted-start strip(":)
ACPI:
status: odd fixes
"West project: acpica":
status: odd fixes
# zephyr-keep-sorted-stop

**nofold**
Disables line folding. By default, indented lines following a main line are
concatenated (folded) together for sorting comparison. The ``nofold`` option
disables this behavior and ignores indented lines.

**ignorecase**
Enables case-insensitive sorting using Python's `str.casefold`_. This allows
mixing uppercase and lowercase items in sorted blocks without causing sort
order violations. Defaults to Python's string ordering if omitted.

.. _str.casefold: https://docs.python.org/3/library/stdtypes.html#str.casefold

Multiple options can be combined on the same marker line:

.. code-block:: rst

.. zephyr-keep-sorted-start re(^\* \w) ignorecase
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: the migration guide uses re(^\w) 🙂

* Shell
Some important message about the shell.

* STM32
Updates for this vendor.
.. zephyr-keep-sorted-stop

twister
-------

Expand Down
12 changes: 10 additions & 2 deletions scripts/ci/check_compliance.py
Original file line number Diff line number Diff line change
Expand Up @@ -2362,7 +2362,7 @@ class KeepSorted(ComplianceTest):

MARKER = "zephyr-keep-sorted"

def block_check_sorted(self, block_data, *, regex, strip, fold):
def block_check_sorted(self, block_data, *, regex, strip, fold, icase):
def _test_indent(txt: str):
return txt.startswith((" ", "\t"))

Expand Down Expand Up @@ -2393,6 +2393,9 @@ def _test_indent(txt: str):
for cont in takewhile(_test_indent, lines[idx + 1 :]):
line += cont.strip()

if icase:
line = line.casefold()

if line < last:
return idx

Expand All @@ -2414,10 +2417,12 @@ def check_file(self, file, fp):
regex_marker = r"re\(([^)]+)\)"
strip_marker = r"strip\(([^)]+)\)"
nofold_marker = "nofold"
ignorecase_marker = "ignorecase"
start_line = 0
regex = None
strip = None
fold = True
icase = False

for line_num, line in enumerate(fp.readlines(), start=1):
if start_marker in line:
Expand All @@ -2436,13 +2441,16 @@ def check_file(self, file, fp):
strip = match.group(1) if match else None

fold = nofold_marker not in line
icase = ignorecase_marker in line
elif stop_marker in line:
if not in_block:
desc = f"{stop_marker} without {start_marker}"
self.fmtd_failure("error", "KeepSorted", file, line_num, desc=desc)
in_block = False

idx = self.block_check_sorted(block_data, regex=regex, strip=strip, fold=fold)
idx = self.block_check_sorted(
block_data, regex=regex, strip=strip, fold=fold, icase=icase
)
if idx >= 0:
desc = f"sorted block has out-of-order line at {start_line + idx}"
self.fmtd_failure("error", "KeepSorted", file, line_num, desc=desc)
Expand Down
Loading