Dev_style
Nvim :help
pages, generated
from source
using the tree-sitter-vimdoc parser.
Header Files
#define
guards to prevent multiple inclusion.
The format of the symbol name should be NVIM_<DIRECTORY>_<FILE>_H
.#ifndef NVIM_FOO_BAR_H #define NVIM_FOO_BAR_H…
#endif // NVIM_FOO_BAR_H
Scoping
int i; i = f(); // BAD: initialization separate from declaration.int j = g(); // GOOD: declaration has initialization.
Nvim-Specific Magic
clint.py
to detect style errors.src/clint.py
is a Python script that reads a source file and identifies
style errors. It is not perfect, and has both false positives and false
negatives, but it is still a valuable tool. False positives can be ignored by
putting // NOLINT
at the end of the line.Other C Features
alloca()
.i++
) in statements.for (int i = 0; i < 3; i++) { } int j = ++i; // OK: ++i is used as an expression.for (int i = 0; i < 3; ++i) { } ++i; // BAD: ++i is used as a statement.
const
pointers whenever possible. Avoid const
on non-pointer parameter definitions.int const *foo
to const int *foo
. They
argue that this is more readable because it's more consistent: it keeps
the rule that const
always follows the object it's describing. However,
this consistency argument doesn't apply in codebases with few
deeply-nested pointer expressions since most const
expressions have only
one const
, and it applies to the underlying value. In such cases, there's
no consistency to maintain. Putting the const
first is arguably more
readable, since it follows English in putting the "adjective" (const
)
before the "noun" (int
).const
first, we do not require it.
But be consistent with the code around you!void foo(const char *p, int i); }int foo(const int a, const bool b) { }
int foo(int *const p) { }
char
, int
, uint8_t
, int8_t
,
uint16_t
, int16_t
, uint32_t
, int32_t
, uint64_t
, int64_t
,
uintmax_t
, intmax_t
, size_t
, ssize_t
, uintptr_t
, intptr_t
, and
ptrdiff_t
.int
for error codes and local, trivial variables only.char
is
implementation defined.uint8_t
, etc.)printf
format placeholders for fixed width types.
Cast to uintmax_t
or intmax_t
if you have to format fixed width integers.char
%hhu
%hhd
int
n/a %d
(u)intmax_t
%ju
%jd
(s)size_t
%zu
%zd
ptrdiff_t
%tu
%td
bool
to represent boolean values.int loaded = 1; // BAD: loaded should have type bool.
if (1 == x) {if (x == 1) { //use this order
if ((x = f()) && (y = g())) {
static void f(void);static void f(void) { … }
<HEADER><PUBLIC FUNCTION DEFINITIONS>
<STATIC FUNCTION DEFINITIONS>
.c.generated.h
, *.h.generated.h
files
contain only non-static function declarations.// src/nvim/foo.c file #include <stddef.h>typedef int FooType;
#ifdef INCLUDE_GENERATED_DECLARATIONS
include “foo.c.generated.h”
#endif
…
// src/nvim/foo.h file #ifndef NVIM_FOO_H #define NVIM_FOO_H
…
#ifdef INCLUDE_GENERATED_DECLARATIONS
include “foo.h.generated.h”
#endif #endif // NVIM_FOO_H
sizeof(void *)
!= sizeof(int)
. Use intptr_t
if you want
a pointer-sized integer.
int64_t
/`uint64_t` member will by default end up being 8-byte aligned on a
64-bit system. If you have such structures being shared on disk between
32-bit and 64-bit code, you will need to ensure that they are packed the
same on both architectures. Most compilers offer a way to alter structure
alignment. For gcc, you can use __attribute__((packed))
. MSVC offers
#pragma pack()
and __declspec(align())
.
LL
or ULL
suffixes as needed to create 64-bit constants. For
example:int64_t my_value = 0x123456789LL; uint64_t my_mask = 3ULL << 48;sizeof ~
sizeof(varname)
to sizeof(type)
.sizeof(varname)
when you take the size of a particular variable.
sizeof(varname)
will update appropriately if someone changes the variable
type either now or later. You may use sizeof(type)
for code unrelated to any
particular variable, such as code that manages an external or internal data
format where a variable of an appropriate C type is not convenient.Struct data; memset(&data, 0, sizeof(data));memset(&data, 0, sizeof(Struct));
if (raw_size < sizeof(int)) { fprintf(stderr, “compressed record not big enough for count: %ju”, raw_size); return false; }
Naming
int price_count_reader; // No abbreviation. int num_errors; // "num" is a widespread convention. int num_dns_connections; // Most people know what "DNS" stands for.int n; // Meaningless. int nerr; // Ambiguous abbreviation. int n_comp_conns; // Ambiguous abbreviation. int wgc_connections; // Only your group knows what this stands for. int pc_reader; // Lots of things can be abbreviated “pc”. int cstmr_id; // Deletes internal letters.
_
).my_useful_file.c getline_fix.c // OK: getline refers to the glibc function.C files should end in
.c
and header files should end in .h
./usr/include
, such as db.h
.http_server_logs.h
rather than logs.h
.MyExcitingStruct
.struct my_exciting_struct
.struct my_struct { ... }; typedef struct my_struct MyAwesomeStruct;
my_exciting_local_variable
.string table_name; // OK: uses underscore. string tablename; // OK: all lowercase.string tableName; // BAD: mixed case.
struct url_table_properties { string name; int num_entries; }
g_
.k
followed by mixed case: kDaysInAWeek
.k
followed by words with uppercase first letters:const int kDaysInAWeek = 7;
my_exceptional_function()
. All functions in the same header file
should have a common prefix.os_unix.h
:void unix_open(const char *path); void unix_user_id(void);If your function crashes upon an error, you should append
or_die
to the
function name. This only applies to functions which could be used by
production code and to errors that are reasonably likely to occur during
normal operation.kEnumName
.enum url_table_errors { kOK = 0, kErrorOutOfMemory, kErrorMalformedInput, };
MY_MACRO_THAT_SCARES_CPP_DEVELOPERS
.#define ROUND(x) ... #define PI_ROUNDED 5.0
Comments
//
-style syntax only.// This is a comment spanning // multiple lines f();
.h
file will describe the variables and functions that are
declared in the file with an overview of what they are for and how they
are used. A .c
file should contain more information about implementation
details or discussions of tricky algorithms. If you feel the
implementation details or a discussion of the algorithms would be useful
for someone reading the .h
, feel free to put it there instead, but
mention in the .c
that the documentation is in the .h
file..h
and the .c
. Duplicated
comments diverge./// A brief description of this file. /// /// A longer description of this file. /// Be very generous here.
/// Window info stored with a buffer. /// /// Two types of info are kept for a buffer which are associated with a /// specific window: /// 1. Each window can have a different line number associated with a /// buffer. /// 2. The window-local options for a buffer work in a similar way. /// The window-info is kept in a list at g_wininfo. It is kept in /// most-recently-used order. struct win_info { /// Next entry or NULL for last entry. WinInfo *wi_next; /// Previous entry or NULL for first entry. WinInfo *wi_prev; /// Pointer to window that did the wi_fpos. Win *wi_win; ... };If the field comments are short, you can also put them next to the field. But be consistent within one struct, and follow the necessary doxygen style.
struct wininfo_S { WinInfo *wi_next; ///< Next entry or NULL for last entry. WinInfo *wi_prev; ///< Previous entry or NULL for first entry. Win *wi_win; ///< Pointer to window that did the wi_fpos. ... };If you have already described a struct in detail in the comments at the top of your file feel free to simply state "See comment at top of file for a complete description", but be sure to have some sort of comment.
.h
file or wherever. It's okay to recapitulate
briefly what the function does, but the focus of the comments should be on
how it does it.// Note that we don't use Doxygen comments here. Iterator *get_iterator(void *arg1, void *arg2) { ... }
/// The total number of tests cases that we run /// through in this regression test. const int kNumTestCases = 6;
// If we have enough memory, mmap the data portion too. mmap_budget = max<int64>(0, mmap_budget - index_->length()); if (mmap_budget >= data_size_ && !MmapData(mmap_chunk_bytes, mlock)) { return; // Error already logged. }
do_something(); // Comment here so the comments line up. do_something_else_that_is_longer(); // Comment here so there are two spaces between // the code and the comment. { // One space before comment when opening a new scope is allowed, // thus the comment lines up with the following comments and code. do_something_else(); // Two spaces before line comments normally. }
bool success = calculate_something(interesting_value, 10, false, NULL); // What are these arguments??
bool success = calculate_something(interesting_value, 10, // Default base value. false, // Not the first time we're calling this. NULL); // No callback.
const int kDefaultBaseValue = 10; const bool kFirstTimeCalling = false; Callback *null_callback = NULL; bool success = calculate_something(interesting_value, kDefaultBaseValue, kFirstTimeCalling, null_callback);
// Now go through the b array and make sure that if i occurs, // the next element is i+1. ... // Geez. What a useless comment.
TODO
comments for code that is temporary, a short-term solution, or
good-enough but not perfect.TODO
s should include the string TODO
in all caps, followed by the name,
email address, or other identifier of the person who can best provide context
about the problem referenced by the TODO
. The main purpose is to have a
consistent TODO
format that can be searched to find the person who can
provide more details upon request. A TODO
is not a commitment that the
person referenced will fix the problem. Thus when you create a TODO
, it is
almost always your name that is given.// TODO(kl@gmail.com): Use a "*" here for concatenation operator. // TODO(Zeke): change this to use relations.If your
TODO
is of the form "At a future date do something" make sure that
you either include a very specific date ("Fix by November 2005") or a very
specific event ("Remove this code when all clients can handle XML
responses.").@deprecated
docstring token.@deprecated
in all caps. The comment goes either before the declaration
of the interface or on the same line as the declaration.@deprecated
, write your name, email, or other identifier in
parentheses.DEPRECATED
will not magically cause any callsites
to change. If you want people to actually stop using the deprecated facility,
you will have to fix the callsites yourself or recruit a crew to help you.Formatting
"\uFEFF"
, is the Unicode zero-width no-break space character, which
would be invisible if included in the source as straight UTF-8.bool retval = do_something(argument1, argument2, argument3);If the arguments do not all fit on one line, they should be broken up onto multiple lines, with each subsequent line aligned with the first argument. Do not add spaces after the open paren or before the close paren:
bool retval = do_something(averyveryveryverylongargument1, argument2, argument3);If the function has many arguments, consider having one per line if this makes the code more readable:
bool retval = do_something(argument1, argument2, argument3, argument4);Arguments may optionally all be placed on subsequent lines, with one line per argument:
if (...) { ... ... if (...) { do_something( argument1, // 4 space indent argument2, argument3, argument4); }In particular, this should be done if the function signature is so long that it cannot fit within the maximum line length.
{
and one space before the }
{}
were the parentheses of a function call with that name. If there is
no name, assume a zero-length name.struct my_struct m = { // Here, you could also break before {. superlongvariablename1, superlongvariablename2, { short, interior, list }, { interiorwrappinglist, interiorwrappinglist2 } };
default
case (in the case of an enumerated value, the compiler will
warn you if any values are not handled). If the default case should never
execute, simply assert
:switch (var) { case 0: ... break; case 1: ... break; default: assert(false); }
return
expression with parentheses.return expr
; only where you would use them inx =
expr;`.return result; return (some_long_condition && another_condition);return (value); // You wouldn’t write var = (value); return(result); // return is not a function!
int x[] = { 0 }; // Spaces inside braces for braced-init-list.
int long_variable = 0; // Don't align assignments. int i = 1;struct my_struct { // Exception: struct arrays. const char *boy; const char *girl; int pos; } my_variable[] = { { “Mia”, “Michael”, 8 }, { “Elizabeth”, “Aiden”, 10 }, { “Emma”, “Mason”, 2 }, };
x = 0; // Assignment operators always have spaces around // them. x = -5; // No spaces separating unary operators and their x++; // arguments. if (x && !y)