Skip to content

Code style

Donato Meoli edited this page Nov 1, 2026 · 3 revisions

Code style

The C++ sources and the .txt configurations of SMS++ have a layout of their own, which the pipeline of every merge request checks: the job style fails, and the merge request cannot be merged, until what it changes is in that layout (a merge request that has to keep a layout of its own says so with [skip style] in its title, and the job does not run). This page says what the layout is, how to put a file in it, and how to have the IDE do it.

The layout

The formatters of the code are those of the repository code-style, which the umbrella holds as the submodule scripts/code-style at a tag, so that the layout changes only when the umbrella moves it. The layout of the C++ code is the one of its .clang-format, i.e., an indentation of one space, a space inside parentheses, brackets and angles (if( x ), v[ i ], std::vector< int >), T * p and T & r, ! x, 79 columns at most, with what its format.py adds to it and clang-format has no option for: the expression of a return and of a throw in parentheses (return( x );, throw( e );), a space before every comma and before the semicolons of a for (f( a , b )), the closing brace of a block on the indentation of the block (the one of a namespace excepted), the opening brace of a function defined out of a class on a line of its own, and the #define of an include guard one space in. The comments, the order of the includes, the string literals and the indentation of the preprocessor directives are left as the author wrote them, and so is a file that says in its first lines that a program writes it (DO NOT EDIT), such as the *_maps.h of MILPSolver.

The layout of a configuration is the one of scripts/cfgformat.py of the umbrella, to which the .code-format at its root hands the .txt files, and which reads it with the grammar of the load() methods of the core (ComputeConfig, BlockConfig, BlockSolverConfig, SimpleConfiguration< T >, the overrides *file +) and writes it again with the box of the head and of the end 77 columns wide and with the name of the file in it, every element on a line of its own and an empty line between two elements, the values and their comments in columns (the ones of the author, when most of the rows of a table share them), and the structural comments of a ComputeConfig (# number of integer parameters, # now all the integer parameters, # [none]) written as everywhere. Every other comment is kept, and nothing that SMS++ reads changes: the script checks that the tokens out of the comments are the same before and after, and a file that it cannot read with the grammar of the core is reported and left as it is.

Formatting a file

From the umbrella,

scripts/smspp-format --since origin/develop <files>

formats a C++ source only in the lines changed since origin/develop, so that the code around them stays as it was written (the layout of the existing code is in part made by hand, e.g., where a long line breaks and how its continuation aligns, and no formatter reproduces it), and a configuration whole; --git does the same with the lines changed since HEAD, --check writes nothing and lists the files that are not in the layout, which is what the job style runs. smspp-format runs code-format of scripts/code-style, which a recursive clone of the umbrella brings along (otherwise git submodule update --init scripts/code-style). The scripts need Python 3 and clang-format 22, e.g.,

pip install clang-format==22.1.5

since the layout clang-format produces changes slightly from one version to the next. The tests of the formatters are in scripts/code-style/tests/ and in scripts/test_cfgformat.py.

On a merge request

When the job style fails, it runs the formatters on its own copy of the files and keeps what they change as the artifact style.patch of the job (style patch among the artifacts of the merge request), which

git apply style.patch

applies in the module. If the variable STYLE_TOKEN of the pipeline (set once for the whole group) holds an access token (of the role Reporter and with the scope api, masked), the job also posts the changes on the merge request: a change of the lines that the merge request adds is a suggestion on them, which its author applies with one click, and the others (e.g., of a configuration, which is formatted whole) go together in one note; the notes of a pipeline replace the ones of the last.

Before a commit

scripts/code-style/hooks/install-hooks .

installs, in the umbrella and in every submodule, a pre-commit hook of git that checks the files of a commit as the job style does, and stops the commit if one is not in the style, printing the command that puts it there (git commit --no-verify commits all the same). The hook is a link to scripts/code-style/hooks/pre-commit, so that it follows the submodule, and it runs the scripts/smspp-format of the umbrella; in a worktree out of the umbrella it finds it through the variable CODE_FORMAT, which names it.

In the IDE

Any IDE that can run an external command on the file it shows can bind smspp-format to the key of its own formatter. In CLion:

  1. Settings | Tools | External Tools, add a tool named smspp-format with program $ProjectFileDir$/scripts/smspp-format, arguments --git $FilePath$ and working directory $ProjectFileDir$, and uncheck Open console for tool output;
  2. Settings | Keymap, duplicate the keymap in use, remove Ctrl+Alt+L from Code | Reformat Code and give it to External Tools | smspp-format.

The file is reformatted on disk and CLion reloads it; with --git only the lines changed since the last commit are touched, as the job style asks.

Clone this wiki locally