summaryrefslogtreecommitdiffstats
path: root/CODINGSTYLE.md
diff options
context:
space:
mode:
Diffstat (limited to 'CODINGSTYLE.md')
-rw-r--r--CODINGSTYLE.md394
1 files changed, 394 insertions, 0 deletions
diff --git a/CODINGSTYLE.md b/CODINGSTYLE.md
new file mode 100644
index 000000000..37e242df0
--- /dev/null
+++ b/CODINGSTYLE.md
@@ -0,0 +1,394 @@
+# Coding Style
+
+Here are some of the basics that we are trying to enforce for our coding style
+and conventions. The existing code (as of the commit that adds these lines) is
+not yet fully consistent to these rules, but following these rules will make
+sure that no one yells at you about your patches.
+
+We have a script that can be used to reformat code to be reasonably close
+to these rules; it's in scripts/whitespace.pl - this script requires
+clang-format to be installed (which sadly isn't installed by default on
+any of our platforms; even on Mac where clang is the default compiler).
+
+At the end of this file are some ideas for your .emacs file (if that's
+your editor of choice) as well as for QtCreator. If you have settings for
+other editors that implement this coding style, please add them here.
+
+## Basic rules
+
+* all indentation is tabs (set to 8 char) with the exception of
+ continuation lines that are aligned with tabs and then spaces
+
+* all keywords followed by a '(' have a space in between
+ ```
+ if (condition)
+
+ for (i = 0; i < 5; i++)
+ ```
+
+* function calls do NOT have a space between their name and argument
+ ```
+ i = some_function(argument);
+ ```
+
+* usually there is no space on the inside of parenthesis (see examples
+ above)
+
+* function / method implementations have their opening curly braces in
+ column 1
+
+* all other opening curly braces follow at the end of the line, with a
+ space separating them:
+ ```
+ if (condition) {
+ dosomething();
+ dosomethingelse();
+ }
+ ```
+
+* both sides of an if / else clause either use or do not use curly braces:
+ ```
+ if (condition)
+ i = 4;
+ else
+ j = 6;
+
+ if (condition) {
+ i = 6;
+ } else {
+ i = 4;
+ j = 6;
+ }
+ ```
+
+* use space to make visual separation easier
+ ```
+ a = b + 3 + e / 4;
+ ```
+
+* continuation lines have the operator / comma at the end
+ ```
+ if (very_long_condition_1 ||
+ condition_2)
+
+ b = a + (c + d +
+ f + z);
+ ```
+
+* in a C++ constructor initialization list, the colon is on the same line and
+ continuation lines are aligned as the rule above:
+ ```
+ ClassName::ClassName() : x(1),
+ y(2),
+ z(3)
+ {
+ }
+ ```
+
+* unfortunate inconsistency
+ - C code usually uses underscores to structure names
+ ```
+ variable_in_C
+ ```
+ - In contrast, C++ code usually uses camelCase
+ ```
+ variableInCPlusPlus
+ ```
+
+ where the two meet, use your best judgment and go for best consistency
+ (i.e., where does the variable "originate")
+
+* there is a strong preference for lower case file names; sometimes conventions
+ or outside requirements make camelCase filenames the better (or only) choice,
+ but absent such an outside reason all file names should be lower case
+
+* switch statements with blocks are a little bit special (to avoid indenting
+ too far)
+ ```
+ switch (foo) {
+ case FIRST:
+ whatever();
+ break;
+ case SECOND: {
+ int i;
+ for (i = 0; i < 5; i++)
+ do_something(i);
+ }
+ }
+ ```
+
+## Coding conventions
+
+* variable declarations
+ In C code we really like them to be at the beginning of a code block,
+ not interspersed in the middle.
+ in C++ we are a bit less strict about this - but still, try not to go
+ crazy. Notably, in C++ the lifetime of a variable often coincides with the
+ lifetime of a resource (e.g. file) and therefore the variable is defined
+ at the place where the resource is needed.
+
+* The `*`, `&` and `&&` declarators are grouped with the name, not the type
+ (classical C-style) as in `char *string` instead of `char* string`. This
+ reflects the precedence rules of the language: `int &i` means that the name
+ `i` stands for a reference [to an object with type `int`], not that
+ `i` stands for an object of the type [reference to `int`].
+ Although this may seem like hairsplitting (both interpretations
+ have the same effect) it is crucial in the
+ definition of multiple variables, such
+ as
+ ```
+ struct dive *next, **pprev;
+ ```
+
+* In C++ code, we generally use explicit types in variable declarations for clarity.
+ Use `auto` sparingly and only in cases where code readability improves.
+ Two classical examples are:
+ - Iterators, whose type names often are verbose:
+ ```
+ auto it = m_trackers.find(when);
+ ```
+ is not only distinctly shorter than
+ ```
+ QMap<qint64, gpsTracker>::iterator it = m_trackers.find(when);
+ ```
+ it will also continue working if a different data structure is chosen.
+ - If the type is given in the same line anyway. Thus,
+ ```
+ auto service = qobject_cast<QLowEnergyService*>(sender());
+ ```
+ is easier to read than and conveys the same information as
+ ```
+ QLowEnergyService *service = qobject_cast<QLowEnergyService*>(sender());
+ ```
+ - If the variable is a container that is only assigned to a local variable to
+ be able to use it in a range-based for loop
+ ```
+ const auto l = device.serviceUuids();
+ for (QBluetoothUuid id: serviceUuids) {
+ ```
+ The variable has also to be const to avoid that Qt containers will do a
+ deep copy when the range bases for loop will call the begin() method
+ internally.
+
+* text strings
+ The default language of subsurface is US English so please use US English
+ spelling and terminology.
+ User-visible strings should be passed to the tr() function to enable
+ translation into other languages.
+ - like this
+ ```
+ QString msgTitle = tr("Submit user survey.");
+ ```
+ - rather than
+ ```
+ QString msgTitle = "Submit user survey.";
+ ```
+
+ This works by default in classes (indirectly) derived from QObject. Each
+ string to be translated is associated with a context, which corresponds
+ to the class name. Classes that are not derived from QObject can generate
+ the tr() functions by using the `Q_DECLARE_TR_FUNCTIONS` macro:
+ ```
+ #include <QCoreApplication>
+
+ class myClass {
+ Q_DECLARE_TR_FUNCTIONS(gettextfromC)
+ ...
+ };
+ ```
+
+ As an alternative, which also works outside of class context, the tr()
+ function of a different class can be called. This avoids creating multiple
+ translations for the same string:
+ ```
+ gettextFromC::tr("%1km")
+ ```
+
+ The gettextFromC class in the above example was created as a catch-all
+ context for translations accessed in C code. But it can also be used
+ from C++ helper functions. To use it from C, include the "core/gettext.h"
+ header and invoke the translate() macro:
+ ```
+ #include "core/gettext.h"
+
+ report_error(translate("gettextFromC", "Remote storage and local data diverged"));
+ ```
+ It is crucial to pass "gettextFromC" as a first macro argument so that Qt
+ is able to associate the string with the correct context.
+ The translate macro returns a cached C-style string, which is generated at runtime
+ when the particular translation string is encountered for the first time.
+ It remains valid during the whole application's life time.
+
+ Outside of function context, the `QT_TRANSLATE_NOOP` macro can be used as in
+ ```
+ struct ws_info_t ws_info[100] = {
+ { QT_TRANSLATE_NOOP("gettextFromC", "integrated"), 0 },
+ { QT_TRANSLATE_NOOP("gettextFromC", "belt"), 0 },
+ { QT_TRANSLATE_NOOP("gettextFromC", "ankle"), 0 },
+ { QT_TRANSLATE_NOOP("gettextFromC", "backplate"), 0 },
+ { QT_TRANSLATE_NOOP("gettextFromC", "clip-on"), 0 },
+ };
+ ```
+ Note that here, the texts will be scheduled for translation with the "gettextFromC"
+ context, but the array is only initialized with the original text. The actual
+ translation has to be performed later in code. For C-code, the `QT_TRANSLATE_NOOP`
+ macro is defined in the "core/gettext.h" header.
+
+* UI text style
+ These guidelines are designed to ensure consistency in presentation within
+ Subsurface.
+ Only the first word of multi-word text strings should be capitalized unless
+ a word would normally be capitalized mid-sentence, like Africa. This applies
+ to all UI text including menus, menu items, tool-tips, button text and label
+ text etc. e.g. "Check for updates" rather than "Check for Updates".
+ We also capitalize Subsurface (NOTE: not SubSurface) when referring to the
+ application itself.
+ Abbreviations should end with a period, e.g. "temp." not "temp" for
+ temperature
+ Numerals in chemical formulae should use subscript characters e.g. O₂ not O2
+ Partial pressures in Subsurface are, by convention, abbreviated with a single
+ "p" rather than 2, as in pO₂ not ppO₂
+ Where more than one term exists for something, please choose the one already
+ in use within Subsurface e.g. Cylinder vs. Tank.
+
+
+* string manipulation
+ * user interface
+ In UI part of the code use of QString methods is preferred, see this pretty
+ good guide in [QString documentation][1]
+ * core components
+ In the core part of the code, C-string should be used.
+ C-string manipulation is not always straightforward specifically when
+ it comes to memory allocation, a set of helper functions has been developed
+ to help with this. Documentation and usage examples can be found in
+ [core/membuffer.h][2]
+
+## Sample Settings
+
+### Emacs
+
+These lines in your .emacs file should get you fairly close when it comes
+to indentation - many of the other rules you have to follow manually
+
+```
+;; indentation
+(defun c-lineup-arglist-tabs-only (ignored)
+ "Line up argument lists by tabs, not spaces"
+ (let* ((anchor (c-langelem-pos c-syntactic-element))
+ (column (c-langelem-2nd-pos c-syntactic-element))
+ (offset (- (1+ column) anchor))
+ (steps (floor offset c-basic-offset)))
+ (* (max steps 1)
+ c-basic-offset)))
+
+(add-hook 'c-mode-common-hook
+ (lambda ()
+ ;; Add kernel style
+ (c-add-style
+ "linux-tabs-only"
+ '("linux" (c-offsets-alist
+ (arglist-cont-nonempty
+ c-lineup-gcc-asm-reg
+ c-lineup-arglist-tabs-only))))))
+
+(add-hook 'c-mode-hook
+ (lambda ()
+ (let ((filename (buffer-file-name)))
+ ;; Enable kernel mode for the appropriate files
+ (setq indent-tabs-mode t)
+ (c-set-style "linux-tabs-only"))))
+
+(add-hook 'c++-mode-hook
+ (lambda ()
+ (let ((filename (buffer-file-name)))
+ ;; Enable kernel mode for the appropriate files
+ (setq indent-tabs-mode t)
+ (c-set-style "linux-tabs-only"))))
+```
+
+### QtCreator
+
+These settings seem to get indentation right in QtCreator. Making TAB
+always adjust indent makes it hard to add hard tabs before '\' when
+creating continuing lines. Copying a tab with your mouse / ctrl-C and
+inserting it with ctrl-V seems to work around that problem (use Command
+instead of ctrl on your Mac)
+Save this XML code below to a file, open Preferences (or Tools->Options)
+in QtCreator, pick C++ in the left column and then click on Import...
+to open the file you just created. Now you should have a "Subsurface"
+style that you can select which should work well for our coding style.
+
+```
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QtCreatorCodeStyle>
+<!-- Written by QtCreator 3.0.0, 2014-02-27T07:52:57. -->
+<qtcreator>
+ <data>
+ <variable>CodeStyleData</variable>
+ <valuemap type="QVariantMap">
+ <value type="bool" key="AlignAssignments">false</value>
+ <value type="bool" key="AutoSpacesForTabs">false</value>
+ <value type="bool" key="BindStarToIdentifier">true</value>
+ <value type="bool" key="BindStarToLeftSpecifier">false</value>
+ <value type="bool" key="BindStarToRightSpecifier">false</value>
+ <value type="bool" key="BindStarToTypeName">false</value>
+ <value type="bool" key="ExtraPaddingForConditionsIfConfusingAlign">false</value>
+ <value type="bool" key="IndentAccessSpecifiers">false</value>
+ <value type="bool" key="IndentBlockBody">true</value>
+ <value type="bool" key="IndentBlockBraces">false</value>
+ <value type="bool" key="IndentBlocksRelativeToSwitchLabels">false</value>
+ <value type="bool" key="IndentClassBraces">false</value>
+ <value type="bool" key="IndentControlFlowRelativeToSwitchLabels">true</value>
+ <value type="bool" key="IndentDeclarationsRelativeToAccessSpecifiers">true</value>
+ <value type="bool" key="IndentEnumBraces">false</value>
+ <value type="bool" key="IndentFunctionBody">true</value>
+ <value type="bool" key="IndentFunctionBraces">false</value>
+ <value type="bool" key="IndentNamespaceBody">false</value>
+ <value type="bool" key="IndentNamespaceBraces">false</value>
+ <value type="int" key="IndentSize">8</value>
+ <value type="bool" key="IndentStatementsRelativeToSwitchLabels">true</value>
+ <value type="bool" key="IndentSwitchLabels">false</value>
+ <value type="int" key="PaddingMode">2</value>
+ <value type="bool" key="SpacesForTabs">false</value>
+ <value type="int" key="TabSize">8</value>
+ </valuemap>
+ </data>
+ <data>
+ <variable>DisplayName</variable>
+ <value type="QString">Subsurface</value>
+ </data>
+</qtcreator>
+```
+
+### Vim
+
+As everybody knows vim is a way better editor than emacs and thus needs to be
+in this file too. Put this into your .vimrc and this should produce something
+close to our coding standards.
+
+```
+" Subsurface coding style
+filetype plugin indent on
+filetype detect
+set cindent tabstop=8 shiftwidth=8 cinoptions=l1,:0,(0,g0
+" TODO: extern "C" gets indented
+
+" And some sane defaults, optional, but quite nice
+set nocompatible
+syntax on
+colorscheme default
+set hls
+set is
+
+" The default blue is just impossible to see on a black terminal
+highlight Comment ctermfg=Brown
+
+" clearly point out when someone have trailing spaces
+highlight ExtraWhitespace ctermbg=red guibg=red
+
+" Show trailing whitespace and spaces before a tab:
+match ExtraWhitespace /\s\+$\| \+\ze\t/
+```
+
+[1]: http://doc.qt.io/qt-5/qstring.html#manipulating-string-data
+[2]: https://github.com/Subsurface-divelog/subsurface/blob/master/core/membuffer.h