Recommendations for clean code bases
Internally, elementary uses the following code style guide to ensure that code is consistently formatted both internally and across projects. Consistent and easily-legible code makes it easier for newcomers to learn and contribute. We'd like to recommend that in the spirit of Open Source collaboration, all Vala apps written in the wider ecosystem also follow these guidelines.
White space comes before opening parentheses or braces:
An exception is admitted for Gettext-localized strings, where no space should go between the underscore and the opening parenthese:
Whitespace goes between numbers and operators in all math-related code.
Lines consisting of closing brackets (} or )) should be followed by an empty line, except when followed by another closing bracket or an else statement.
Vala code is indented using 4 spaces for consistency and readability.
In classes, functions, loops and general flow control, the first brace is on the end of the first line ("One True Brace Style"), followed by the indented code, and a line closing the function with a brace:
On conditionals and loops, always use braces even if there's only one line of code:
Cuddled else and else if:
If you are checking the same variable more than twice, use switch/case instead of multiple else/if:
Markup languages like HTML, XML, and YML should use two-space indentation since they are much more verbose and likely to hit line-length issues sooner.
A file should only contain one public class.
All files have the same name as the class in them. For example, a file containing the class AbstractAppGrid should be called "AbstractAppGrid.vala"
Classes should be named in a descriptive way, and include the name of any parent classes. For example, a class that subclasses Gtk.ListBoxRow and displays the names of contacts should be called ContactRow.
Comments are either on the same line as the code they reference or in a special line.
Comments are indented alongside the code, and obvious comments do more harm than good.
Sometimes detailed descriptions in the context of translatable strings are necessary for disambiguation or to help in the creation of accurate translations. For these situations use /// TRANSLATORS: comments.
Variable and function names are all lower case and separated by underscores:
Classes and enums are Upper Camel Case (aka Pascal Case):
Constants and enum members should be all uppercase and separated by underscores:
The values of constant strings (such as when used with GLib.Action) should be lowercase and separated with dashes:
Avoid using as keyword when casting as it might give null as result, which could be forgotten to check.
In places or operations where you would otherwise use get or set , you should make use of = instead.
For example, instead of using
you should use
This is especially clearer when initializing an object with many properties. Avoid the following
and instead do this
This goes for creating methods inside of classes as well. Instead of
you should use
or, where you need some extra logic in the getters and setters:
Preferring properties in classes enables the use of between classes instead of needing to create signals and handle changing properties manually.
Referring to GLib is not necessary. If you want to print something instead of:
You can use
Avoid using literals when formatting strings:
Instead, prefer printf style placeholders:
Warnings in Vala use printf syntax by default:
Gtk widgets are intended to respond to click events that can be described as "press + release" instead of "press". Usually it is better to respond to toggle and release events instead of press.
Ideally, lines should have no more than 80 characters per line, because this is the default terminal size. However, as an exception, more characters could be added, because most people have wide-enough monitors nowadays. The hard limit is 120 characters.
For methods that take multiple arguments, it is not uncommon to have very long line lengths. In this case, treat parenthesis like brackets and split lines at commas like so:
If you are using elementary Code or your code editor supports , you can use this as a default .editorconfig file in your projects:
public string get_text () {}
if (a == 5) {
return 4;
}
for (i = 0; i < maximum; i++) {}
my_function_name ();
var my_instance = new Object ();// Space before parentheses since it's normal method:
button.label = set_label_string ();
// No space before parentheses since it's gettext-localized string:
button.tooltip_text = _("Download");c = (n * 2) + 4;if (condition) {
// ...
} else {
// ...
}
// other codepublic int my_function (int a, string b, long c, int d, int e) {
if (a == 5) {
b = 3;
c += 2;
return d;
}
return e;
}if (my_var > 2) {
print ("hello\n");
}if (a == 4) {
b = 1;
print ("Yay");
} else if (a == 3) {
b = 3;
print ("Not so good");
}switch (week_day) {
case "Monday":
message ("Let's work!");
break;
case "Tuesday":
case "Wednesday":
message ("What about watching a movie?");
break;
default:
message ("You don't have any recommendation.");
break;
}<component type="desktop">
<name>Calendar</name>
<description>
<p>A slim, lightweight calendar app that syncs and manages multiple calendars in one place, like Google Calendar, Outlook and CalDAV.</p>
</description>
<releases>
<release version="5.0" date="2019-02-28" urgency="medium">
<description>
<p>Add a search entry for calendars</p>
</description>
</release>
</releases>
<screenshots>
<screenshot type="default">
<image>https://raw.githubusercontent.com/elementary/calendar/master/data/screenshot.png</image>
</screenshot>
</screenshots>
</component>/* User chose number five */
if (c == 5) {
a = 3;
b = 4;
d = -1; // Values larger than 5 are undefined
}/// TRANSLATORS: The first %s is search term, the second is the name of default browser
title = _("Search for %s in %s").printf (query, get_default_browser_name ());var my_variable = 5;
public void my_function_name () {
do_stuff ();
}public class UserListBox : Gtk.ListBox {
private enum OperatingSystem {
ELEMENTARY_OS,
UBUNTU
}
}public const string UBUNTU = "ubuntu";
private enum OperatingSystem {
ELEMENTARY_OS,
UBUNTU
}public const string ACTION_GO_BACK = "action-go-back";/* OK */
((Gtk.Entry) widget).max_width_chars
/* NOT OK as this approach requires a check for null */
(widget as Gtk.Entry).max_width_charsset_can_focus (false);can_focus = false;var label = new Gtk.Label ("Test Label");
label.set_ellipsize (Pango.EllipsizeMode.END);
label.set_valign (Gtk.Align.END);
label.set_width_chars (33);
label.set_xalign (0);var label = new Gtk.Label ("Test Label") {
ellipsize = Pango.EllipsizeMode.END,
valign = Gtk.Align.END,
width_chars = 33,
xalign = 0
};private int _number;
public int get_number () {
return _number;
}
public void set_number (int value) {
_number = value;
}public int number { get; set; }private int _number;
public int number {
get {
// We can run extra code here before returning the property. For example,
// we can multiply it by 2
return _number * 2;
}
set {
// We can run extra code here before/after updating the value. For example,
// we could check the validity of the new value or trigger some other state
// changes
_number = value;
}
}GLib.print ("Hello World");print ("Hello World");var string = @"Error parsing config: $(config_path)";var string = "Error parsing config: %s".printf (config_path);critical ("Error parsing config: %s", config_path);var message_dialog = new Granite.MessageDialog.with_image_from_icon_name (
"Basic Information and a Suggestion",
"Further details, including information that explains any unobvious consequences of actions.",
"phone",
Gtk.ButtonsType.CANCEL
);# EditorConfig <https://EditorConfig.org>
root = true
# elementary defaults
[*]
charset = utf-8
end_of_line = lf
indent_size = tab
indent_style = space
insert_final_newline = true
max_line_length = 80
tab_width = 4
# Markup files
[{*.html,*.xml,*.xml.in,*.yml}]
tab_width = 2When creating a new class, prefer GObject-style construction. This type of class construction separates handling arguments from the main logic of your class. It also ensures that information passed in during construction is still available later, and makes your class more likely to be compatible with GLib's functions like property binding.
A simple class written with GObject-style construction looks like this:
In the above example, MyClass has a public property foo whose type is int, but we've also declared it as { get; construct; }. This shorthand declares the type of access for this property's get and set functions. Since we declared the property as public, get is also public, but we've declared the set function as construct, which means we can only assign its value as a constructor argument.
We want construction arguments to be public to ensure that we can later get information out of a class that was used to construct it, if need be. But it's important to declare limited set access on properties for future maintainability. Since there is no handling for changes in this property in the construct block, setting this property after the class is constructed would have no effect, even if we allowed it by declaring { get; construct set; }.
MyClass also contains a constructor; it describes what arguments are required to construct the class. We've declared here that in order to construct MyClass, we need an int passed in when we initialize a new object such as with var new_class = new MyClass (5);
Inside the constructor, we have a special Object () call, in which we specify a property and its value, Object (foo: foo);. This sets the value of the integer property foo defined earlier to the value received as an argument. It is equivalent to saying this.foo = foo;.
When using GObject-style construction, a constructor should only contain code that parses arguments and sets property values with the Object () call. All other class construction logic happens in the construct block. Code in the construct block runs every time an instance of this object is created, regardless of the constructor used.
To see how this style of class construction scales and keeps code organized, let's look at a more complex example. Say we want to display a list of devices. Each device lives in its own row, which is denoted by the class Row. In this example, sometimes we have access to a Device object which stores the name and icon properties of the device, but for some devices we have to pass in those properties manually to construct our row. We can achieve this by declaring two constructors:
a default constructor that takes two arguments: string name and Icon icon
a separate, named constructor which takes one argument: a Device object
Note that in both cases, we handle the arguments in the constructor, while the actual logic of creating UI widgets lives in the common construct block.
This way, we can have multiple constructors without having to repeat the initialization logic.
public class MyClass : Object {
public int foo { get; construct; }
public MyClass (int foo) {
Object (foo: foo);
}
construct {
if (foo > 0) {
// Do stuff
} else {
// Do other stuff
}
}
}public int foo { get; construct; }public MyClass (int foo) {
Object (foo: foo);
}construct {
if (foo) {
// Do stuff
} else {
// Do other stuff
}
}public class Row : Object {
public string name { get; construct; }
public Icon icon { get; construct; }
public Row (string name, Icon icon) {
Object (
name: name,
icon: icon
);
}
public Row.from_device (Device device) {
Object (
name: device.name,
icon: device.icon
);
}
construct {
var icon = new Gtk.Image.from_gicon (icon, Gtk.IconSize.DND);
var label = new Gtk.Label (name);
}
}public Row (string name, Icon icon) {
Object (
name: name,
icon: icon
);
}
public Row.from_device (Device device) {
Object (
name: device.name,
icon: device.icon
);
}construct {
var icon = new Gtk.Image.from_gicon (icon);
var label = new Gtk.Label (name);
}