CodingStyle

Revision 1 as of 2010-12-07 18:53:45

Clear message

Introduction

This document describes the coding style recommended for the Unity project. It first gives general guidelines, applicable in particular to portions developed in C. Then it describes similar recommendations for modules developed in Vala.

The C++ code standard is enclosed in a separate document for now.

Code

General

C style comments as much as possible please, no '//'! Please make sure the copyright is correctly assigned to Canonical and your name is added to the list of authors of that file. Also, please be careful of merge-requesting code that has warnings in it, we want clean code and clean compiles! Also, most of the time we'll be compiling with -Werror and -Wall, so that should help!

Please wrap at 80 chars for us old-skoolers who use vim/emacs :)

emacs tab settings Smile :) - add this to the top of your files and hopefully your editor will be smart enough to stick to the tab settings it defines, even if you are not using emacs

/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */

C Code

Try and stick to the gnu coding style as much as possible (although we deviate from it in certain cases like structs enums). This is basically 2 space indents, no tabs, brackets also indented and on their own line. We should try and keep the variables at the top of the function.

An example that should include most standard parts is below for C.

static MyGObject *
my_g_object_new (const gchar *name,
                 const gchar *type)
{
  MyGObject *me;
  gint       i;

  for (i = 0; i < 5; i++)
    {
      g_print ("I love you ");
      
      switch (i)
      {
        case 0:
          g_print ("%d times\n", i);
          break;
        case 1:
          g_print ("%d times\n", i);
          break;
        case 2:
          g_print ("%d times\n", i);
          break;
        case 3:
          g_print ("%d times\n", i);
          break;
        case 4:
          g_print ("%d times\n", i);
          break;
        default:
          g_print ("%d times\n", i);
          break;
      }
    }

  me = g_object_new (MY_TYPE_G_OBJECT,
                     "name", name,
                     "type", type,
                     NULL);

  return me;
}

Look at liblauncher/clutk/netbook-launcher for inspiration.

Vala Code

- Tabs for indentation, spaces for positioning OR just two spaces, not sure. - Quite similar to GNU C style

namespace Dash
{
  public class Window : Gtk.Window
  {
     public string  name { get; construct; }
     public string  title;
     
     private WinType _type;
     public  WinType type
      {
        set
          {
            if (value != _type)
              {
                _type = value;
                do_something ();
              }
          }
        get { return _type; };
      }

     private is_active;
     private loves_cheese;

     public Window (string name)
     {
       Object (name:name);

       /* Try not to use 'this.' unless it's absolutely necessary, 
        * as it's implicit in Vala anyway (and there is not much
        * chance of you calling another similar named variable or
        * function thanks to namespaces. It helps keep the code
        * clean
        */
       title = "My Window";
       is_active = true;

       type = new WinType (WinType.NORMAL);

       load_buttons ();

       button_press_event.connect (() =>
         {
           do_something_useful ();
         });
     }

     /* Although private is by default, it's nice to have it explicitly stated */
     private void load_buttons ()
     {
       GLib.List<string> files = UI.get_interface_files ();
       foreach (string file in files)
         {
           try
             {
               Gtk.Widget root = UI.load_root_from_file (file);
               /* "this." not needed */
               pack_start (root, false, false, 0);
             }
           catch (Error e)
             {
               warning ("Unable to load UI '%s': %s", file, e.message);
             }
         }
     }

     private override bool expose_event (Gdk.ExposeEvent event)
     {
       var cr = Gdk.cario_create (this.window);
       cr.set_source_rgba (0.0, 0.0, 0.0, 0.0);
       cr.set_operator (Cairo.Operator.CLEAR);
       cr.paint ();
     
       /* The following is not even close to the actual pattern API */
       var pat = new Cairo.PatternLinear ();
       var i;
       for (i = 0; i < 4; i++)
         {
           pat.add_stop (1.0, 1.0, 1.0, 0.2 * i);
         }
       cr.set_source (pat);

       /* Again, no 'this.' */
       switch (state)
         {
         case (Gtk.StateType.ACTIVE):
           cr.rectangle (0, 0, 100, 100);
           break;

         case (Gtk.StateType.PRELIGHT):
           cr.rectangle (0, 0, 100, 50);
           break;

         default:
           cr.rectangle (0, 0, 100, 25);
           break;
         }
       cr.fill ();
    }
  }
}

Vala Code Style 2

This matches more with C# and Vala upstream (though not everything)

  • Tabs for indentation, spaces for positioning.
  • loops/switches/etc have opening bracket on same line, functions have separate line.
  • Don't use 'this.' unless variable name clash or really long function.
  • Wrap at 120chars instead of 80 chars.

namespace Dash
{
  public class Window : Gtk.Window
  {
     public string  name { get; construct; }
     public string  title;
     
     private WinType _type;
     public  WinType type {
       set {
         if (value != _type) {
           _type = value;
           do_something ();
         }
       }  
       
       get { return _type; };
    }

     private is_active;
     private loves_cheese;

     public Window (string name)
     {
       Object (name:name);

       /* Try not to use 'this.' unless it's absolutely necessary, 
        * as it's implicit in Vala anyway (and there is not much
        * chance of you calling another similar named variable or
        * function thanks to namespaces. It helps keep the code
        * clean
        */
       title = "My Window";
       is_active = true;

       type = new WinType (WinType.NORMAL);

       load_buttons ();

       button_press_event.connect (() => {
           do_something_useful ();
       });
     }

     /* Although private is by default, it's nice to have it explicitly stated */
     private void load_buttons ()
     {
       GLib.List<string> files = UI.get_interface_files ();
       foreach (string file in files) {
         try {
           Gtk.Widget root = UI.load_root_from_file (file);
           /* "this." not needed */
           pack_start (root, false, false, 0);
         }
         catch (Error e) {
           warning ("Unable to load UI '%s': %s", file, e.message);
         }
       }
     }

     private override bool expose_event (Gdk.ExposeEvent event)
     {
       var cr = Gdk.cario_create (this.window);
       cr.set_source_rgba (0.0, 0.0, 0.0, 0.0);
       cr.set_operator (Cairo.Operator.CLEAR);
       cr.paint ();
     
       /* The following is not even close to the actual pattern API */
       var pat = new Cairo.PatternLinear ();
       var i;
       for (i = 0; i < 4; i++) {
           pat.add_stop (1.0, 1.0, 1.0, 0.2 * i);
       }
       cr.set_source (pat);

       /* Again, no 'this.' */
       switch (state) {
         case (Gtk.StateType.ACTIVE):
           cr.rectangle (0, 0, 100, 100);
           break;

         case (Gtk.StateType.PRELIGHT):
           cr.rectangle (0, 0, 100, 50);
           break;

         default:
           cr.rectangle (0, 0, 100, 25);
           break;
       }
       cr.fill ();
    }
  }
}

Commit messages

To make using tools like bzr visualise easier, please structure your commit messages like:

[section] One line describing what you did

added:
  files added
    - What you changed the file

modified:
  files modified
    -What you changed

removed:
  files removed
    - Why you removed the file

If you do bzr commit from the commandline, it will open your favorite editor (set with EDITOR envvar) with added/modified/removed already filled out. You just need to delete the line that starts with '---' and then add the "[section] description" line at the top and annotate the files you changed.

You'll also get a list of 'unknown:' files below removed sometimes. These would be ideally added to bzr ignore, but in the minimum please delete them from your commit message.

An example message:

[tests] Bootstrap tests and add basic Dash.Launcher object

added:
  src/launcher.vala
    - Dash.Launcher (window)

  tests/
  tests/Makefile.am
  tests/test-dash-launcher.vala
    - Basic testing and make check, make check-report commands

modified:
  .bzrignore
    - Update ignores

  Makefile.am
    - Added support for check-report

  configure.ac
    - Add support for tests dir

  src/Makefile.am
    - Create static libdash-launcher-static.la lib for tests

  src/main.vala
    - Use Dash.Launcher

Random

Makefiles

Please add files in alphabetical order in Makefile.am, adding a file-per-line if you can't fit everything on one line without wrapping

Gtk-Doc

All public functions and classes should be documented using gtk-doc comments. Properties and signals are self-documenting. Details on gtk-doc syntax can be found here: http://library.gnome.org/devel/gtk-doc-manual/stable/documenting.html.en

Example method documentation

/**
 * launcher_foobar_get_value_at_index
 * @foobar: a #LauncherFoobar objecct
 * @index: an Integer representing the index to use
 *
 * Provides a method to get the data in @foobar at position @index
 *
 * Returns: a #LauncherBazItem
 */
LauncherBazItem *
launcher_foobar_get_value_at_index (LauncherFoobar *foobar, gint index) 
{
  ...
}

Gobject Introspection

GObject introspection is something we want for our libraries (but not so much for applications), It allows for bindings to be built for vala, python, javascript, perl, scheme and even QT!, mostly at runtime as well. Whilst gobject-introspection is handled with a scanner that's fairly robust, it can't detect certain logic behind some parameter declarations, out variables and ownership transfers being the main problem areas. However using gobject introspection annotations in your gtk-doc you can make sure the scanner knows what you want a method to do.

the syntax is described here: http://live.gnome.org/GObjectIntrospection/Annotations

Situations where you may want to use annotations

  • In/Out Parameters
  • Allowing NULL in parameters
  • Ownership transferring
  • Defining the element types in containers
  • Array parameters and the array properties (null terminated, size defined in another parameter and such)

Example:

/**
 * launcher_foobar_get_width_height
 * @foobar: a #LauncherFoobar object
 * @width: (out): an out integer
 * @height: (out): an out integer
 * 
 * provides the @width and @height of @foobar
 *
 */
void
launcher_foobar_get_width_height (LauncherFoobar *foobar, gint *width, gint *height) 
{
  ...
}