This site has been retired. For up to date information, see handbook.gnome.org or gitlab.gnome.org.


[Home] [TitleIndex] [WordIndex

Geary Vala Coding Conventions

Geary is written in Vala. The following guidelines are shaped toward describing our preferred coding style for Vala.

The goals of these coding conventions are consistency and readability for a team of coders, in the service of producing quality code. There are few hard-and-fast rules here. Use good judgement when stretching their boundaries or violating them outright. Ask if you’re unsure.

Formatting

Ordering

The members of each class should appear in the following order:

  1. consts
  2. enums
  3. inner classes
  4. static properties
  5. static fields
  6. instance properties
  7. instance fields
  8. signals
  9. constructors
  10. destructor (optional)
  11. methods

Within them, members should be blocked in order of:

  1. public
  2. protected
  3. internal
  4. private

Basics

public class Foo {

    private string? bar = null;

    public signal void baz();

    public Foo(BazListener baz_sink) {
        this.baz.connect(baz_sink.on_baz);
    }

    private void frob(string barish) {
        ...
        baz(this.bar);
        debug("Just bazzed: %s/%s".printf(this.bar, barish));
    }
}

Right:

int x = get_x();
int y = get_y();

int width, height;
if (is_tall()) {
    width = 0;
    height = 100;
} else {
    width = 100;
    height = 0;
}

Right:

Gee.Set<Geary.Email.Identifier> extract_email_identifiers(Gee.Collection<Geary.App.Conversation> targets) {
    var ids = new Gee.HashSet<Geary.Email.Identifier>()
    foreach (var target in targets) {
        ids.add_all(target.get_email_ids());
    }
    return ids;
}

Wrong:

class ConversationList {

    ...

    void update_email_parts() {
        var all_email = get_selected_email();
        all_email.add_all();
        // Wrong: Double inference from email to all_email
        foreach (var email in all_email) {
            // Wrong: Email class is declared in another file
            foreach (var part in email.parts) {
                ..
            }
        }
    }

}

Naming

Right:

Gtk.Window main_window;

Discouraged:

Gtk.Window w;
Gtk.Window window;

Braces

Right:

void foo() {
    for (int x = 0 ; x &lt; 10 ; ++x) {
        ...
    }
}

Wrong:

void foo()
{
    for (int x = 0 ; x &lt; 10 ; ++x)
    {
        ...
    }
}

Right:

if (x == 4) {
    ...
} else {
    ...
}

Wrong:

if (x == 4) {
    ...
} else ++y;

if (x == 4) {
    ...
} else
    ++y;

if (x == 4) {
    ...
}
else {
    ...
}

Right:

try {
    ...
} catch (Error e) {
    ...
}

Right:

return x;

Right:

return a + b;

Right:

if (x == 3) {
   ++x;
}

Wrong:

if (x == 3)
   ++x;

if (x == 3) ++x;

Spacing

.
.
.
public string foo = null;


public signal void bar();
.
.
.

Right:

a = x + y;

if (x == y)
    ...

Wrong:

a = x+y;

if (x==y)
    ...

Right:

++i;
x = j++;

Wrong:

++ i;
x = j ++;

Right:

left = !right;
stopped = !process.is_running();

Wrong:

left = ! right;
stopped = ! process.is_running();

Right:

int i[10];
x = i[4];

Wrong:

int i [10];
x = i [4];

Right:

int i = (int) f;

Wrong:

int i = (int)f;

* Keywords such as if, for, and while are followed by a space, then a left parenthesis.

Right:

if (x == 4)
    return;

Wrong:

if(x == 4)
    return;

if( x == 4 )
    return;

Right:

int count;
bool is_complete;
Window main_window;

Wrong:

int    count;
bool   is_complete;
Window main_window;

Right:

int add(int a, int b) {
    ...
}

int i = add(4, 5);

Wrong:

int add (int a, int b) {
    ...
}

int i = add ( 4, 5 );

namespace City {

class House {
    int xyz;
    ...
}

}

Comments

Acceptable:

int count; // number of widgets in the conglomeration

Better:

// number of widgets in the conglomeration
int count;

Wrong:

int count; //number of widgets in the conglomeration
int count;// number of widgets in the conglomeration

Language Features

Casting

if (obj is Gtk.Widget)
    // do something

try {
    ...
} catch (Error err) {
    bool is_cancelled = err is IOError.CANCELLED;
    bool is_ioerror = err is IOError;
}

Gtk.Widget? widget = obj as Gtk.Widget;
if (widget != null) {
    widget.show_all();
}

Or using var (note it is implicitly nullable, and still needs to be tested as such):

var widget = obj as Gtk.Widget;
if (widget != null) {
    widget.show_all();
}

Wrong:

Gtk.Widget widget = obj as Widget;
widget.show_all();

Right:

Gtk.Widget widget = (Gtk.Widget) obj;
widget.show_all();

Wrong:

Gtk.Widget widget = (Gtk.Widget) obj;
if (widget != null) {
    widget.show_all();
}

Wrong:

assert(obj is Gtk.Widget);
Gtk.Widget widget = (Gtk.Widget) obj;

Switch statements

Right:

enum Suite { SPADES, …; }

Suite current = …;

switch (current) {
case SPADES:
    do_something();
    break;

case HEARTS:
case DIAMONDS:
    do_something_else();
    return;

default:
    // do nothing for other suits
    break;
}

Wrong:

switch (suit) {
    case Suit.SPADES:
        do_something();
        break;

    case Suit.HEARTS:
        do_something();
    return;
    default:
    break;
}

Signals

Right:

public signal void plain_signal();

public virtual signal void virtual_signal() {
}

public signal void another_plain_signal();

Wrong:

public signal void plain_signal();
public virtual signal void virtual_signal() {}
public signal void another_plain_signal();

Right:

search_menu.activate.connect(on_search_menu_activated);

Right:

public signal void count_changed(int new_count);

protected virtual void notify_count_changed(int new_count) {
count_changed(new_count);
}

This allows for a subclass to override notify_count_changed and update its internal state before or after the signal has fired.

Properties

Right:

public int apple_count { get; private set; } // default set in constructor

public int orange_count { get; set; default = 0 }

public int banana_count {
    get {
        return (banana_list != null) ? banana_list.size : 0;
    }
}

private Gee.List<Banana>? banana_list = null;

/** {@inheritDoc} */ 
public Foo current_fooish {
    get {
        return this._current_fooish;
    }
}
private Foo _current_fooish = null;

Closures (anonymous methods)

In general, closures should only be used to implement short “glue” code blocks to hook up callbacks and signal handlers with class methods. They allow adapting the callback's API (parameters and return type) to the class's API, without needing to implement an additional method for that. This keeps related code together, making it easier to understand and maintain.

Right:

window.destroy.connect(() => { debug("destroyed"); });
window.draw.connect((ctx) => {
    debug("draw");
    ctx.clear();
});

Wrong:

window.destroy.connect((window_param) => {debug("destroyed");});
window.draw.connect((ctx) => {
    debug("draw"); ctx.clear();
});

Default arguments

Vala supports default arguments. As convenient as this feature can be, it can also introduce bugs.

Other language features

Valadoc

We’re making a concerted effort to document the Geary engine with Valadoc, and having documentation comments for public symbols in Geary's client is preferred. Similar to Javadoc, Valadoc uses specially-formatted comments that can be parsed by tools to auto-generate documentation.

/**
 * {@inheritDoc}
 */

Additional notes about the symbol should follow the taglet.

$ ninja -C $BUILD_DIR valadoc

This will generate HTML API documentation in the $BUILD_DIR/src/valadoc directory.

Basics

* A protypical Valadoc for all types of code objects looks like this:

/**
 * A single line summary with a period.
 *
 * Longer (and, for simple code objects, optional) summary explaining
 * the class/method/property in greater detail. Like code lines, the
 * summary lines should not exceed 80 characters per line.
 */

* Use HTML links whenever appropriate if the code is based off a specification or foreign API.

Classes

Each class should have a summary Valadoc. Place a single blank line between its Valadoc and the class declaration.

/** 
 * A representation of the IMAP APPEND command.
 *
 * See [[http://tools.ietf.org/html/rfc3501#section-6.3.11]] 
 */
public class Geary.Imap.AppendCommand : Command {

Other symbols

/**
 * Returns the direction of the overhead oscillating fnortiner.
 */
public bool clockwise { get; private set; }

public class Fnortiner {
    /**
     * Describes the {@link Fnortiner}’s position in the {@link Gizmo}.
     */
    public enum Position {
        /**
         * Indicates the {@link Fnortiner} is upside-down.
         */
        NORMAL,
        /**
         * Indicates the {@link Fnortiner} is inverted.
         */
        REVERSED
    }
}

Note how the containing class is linked in the Valadoc.


2024-10-23 10:58