Blame HACKING.md

Packit Service ed5168
# Coding guide
Packit Service ed5168
Packit Service ed5168
Our goal is to have all JavaScript code in GNOME follow a consistent style. In
Packit Service ed5168
a dynamic language like JavaScript, it is essential to be rigorous about style
Packit Service ed5168
(and unit tests), or you rapidly end up with a spaghetti-code mess.
Packit Service ed5168
Packit Service ed5168
## A quick note
Packit Service ed5168
Packit Service ed5168
Life isn't fun if you can't break the rules. If a rule seems unnecessarily
Packit Service ed5168
restrictive while you're coding, ignore it, and let the patch reviewer decide
Packit Service ed5168
what to do.
Packit Service ed5168
Packit Service ed5168
## Indentation, braces and whitespace
Packit Service ed5168
Packit Service ed5168
* Use four-space indents.
Packit Service ed5168
* Braces are on the same line as their associated statements.
Packit Service ed5168
* You should only omit braces if *both* sides of the statement are on one line.
Packit Service ed5168
* One space after the `function` keyword.
Packit Service ed5168
* No space between the function name in a declaration or a call.
Packit Service ed5168
* One space before the parens in the `if` statements, or `while`, or `for` loops.
Packit Service ed5168
Packit Service ed5168
```javascript
Packit Service ed5168
    function foo(a, b) {
Packit Service ed5168
        let bar;
Packit Service ed5168
Packit Service ed5168
        if (a > b)
Packit Service ed5168
            bar = do_thing(a);
Packit Service ed5168
        else
Packit Service ed5168
            bar = do_thing(b);
Packit Service ed5168
Packit Service ed5168
        if (var == 5) {
Packit Service ed5168
            for (let i = 0; i < 10; i++) {
Packit Service ed5168
                print(i);
Packit Service ed5168
            }
Packit Service ed5168
        } else {
Packit Service ed5168
            print(20);
Packit Service ed5168
        }
Packit Service ed5168
    }
Packit Service ed5168
```
Packit Service ed5168
Packit Service ed5168
## Semicolons
Packit Service ed5168
Packit Service ed5168
JavaScript allows omitting semicolons at the end of lines, but don't. Always
Packit Service ed5168
end statements with a semicolon.
Packit Service ed5168
Packit Service ed5168
## js2-mode
Packit Service ed5168
Packit Service ed5168
If using Emacs, do not use js2-mode. It is outdated and hasn't worked for a
Packit Service ed5168
while. emacs now has a built-in JavaScript mode, js-mode, based on
Packit Service ed5168
espresso-mode. It is the de facto emacs mode for JavaScript.
Packit Service ed5168
Packit Service ed5168
## File naming and creation
Packit Service ed5168
Packit Service ed5168
For JavaScript files, use lowerCamelCase-style names, with a `.js` extension.
Packit Service ed5168
Packit Service ed5168
We only use C where gjs/gobject-introspection is not available for the task, or
Packit Service ed5168
where C would be cleaner. To work around limitations in
Packit Service ed5168
gjs/gobject-introspection itself, add a new method in `shell-util.[ch]`.
Packit Service ed5168
Packit Service ed5168
Like many other GNOME projects, we prefix our C source filenames with the
Packit Service ed5168
library name followed by a dash, e.g. `shell-app-system.c`. Create a
Packit Service ed5168
`-private.h` header when you want to share code internally in the
Packit Service ed5168
library. These headers are not installed, distributed or introspected.
Packit Service ed5168
Packit Service ed5168
## Imports
Packit Service ed5168
Packit Service ed5168
Use UpperCamelCase when importing modules to distinguish them from ordinary
Packit Service ed5168
variables, e.g.
Packit Service ed5168
```javascript
Packit Service ed5168
    const GLib = imports.gi.GLib;
Packit Service ed5168
```
Packit Service ed5168
Imports should be categorized into one of two places. The top-most import block
Packit Service ed5168
should contain only "environment imports". These are either modules from
Packit Service ed5168
gobject-introspection or modules added by gjs itself.
Packit Service ed5168
Packit Service ed5168
The second block of imports should contain only "application imports". These
Packit Service ed5168
are the JS code that is in the gnome-shell codebase,
Packit Service ed5168
e.g. `imports.ui.popupMenu`.
Packit Service ed5168
Packit Service ed5168
Each import block should be sorted alphabetically. Don't import modules you
Packit Service ed5168
don't use.
Packit Service ed5168
```javascript
Packit Service ed5168
    const { GLib, Gio, St } = imports.gi;
Packit Service ed5168
Packit Service ed5168
    const Main = imports.ui.main;
Packit Service ed5168
    const Params = imports.misc.params;
Packit Service ed5168
    const Tweener = imports.ui.tweener;
Packit Service ed5168
    const Util = imports.misc.util;
Packit Service ed5168
```
Packit Service ed5168
The alphabetical ordering should be done independently of the location of the
Packit Service ed5168
location. Never reference `imports` in actual code.
Packit Service ed5168
Packit Service ed5168
## Constants
Packit Service ed5168
Packit Service ed5168
We use CONSTANTS_CASE to define constants. All constants should be directly
Packit Service ed5168
under the imports:
Packit Service ed5168
```javascript
Packit Service ed5168
    const MY_DBUS_INTERFACE = 'org.my.Interface';
Packit Service ed5168
```
Packit Service ed5168
Packit Service ed5168
## Variable declaration
Packit Service ed5168
Packit Service ed5168
Always use either `const` or `let` when defining a variable.
Packit Service ed5168
```javascript
Packit Service ed5168
    // Iterating over an array
Packit Service ed5168
    for (let i = 0; i < arr.length; ++i) {
Packit Service ed5168
        let item = arr[i];
Packit Service ed5168
    }
Packit Service ed5168
Packit Service ed5168
    // Iterating over an object's properties
Packit Service ed5168
    for (let prop in someobj) {
Packit Service ed5168
        ...
Packit Service ed5168
    }
Packit Service ed5168
```
Packit Service ed5168
Packit Service ed5168
If you use "var" then the variable is added to function scope, not block scope.
Packit Service ed5168
See [What's new in JavaScript 1.7](https://developer.mozilla.org/en/JavaScript/New_in_JavaScript/1.7#Block_scope_with_let_%28Merge_into_let_Statement%29)
Packit Service ed5168
Packit Service ed5168
## Classes
Packit Service ed5168
Packit Service ed5168
There are many approaches to classes in JavaScript. We use standard ES6 classes
Packit Service ed5168
whenever possible, that is when not inheriting from GObjects.
Packit Service ed5168
```javascript
Packit Service ed5168
    var IconLabelMenuItem = class extends PopupMenu.PopupMenuBaseItem {
Packit Service ed5168
        constructor(icon, label) {
Packit Service ed5168
            super({ reactive: false });
Packit Service ed5168
            this.actor.add_child(icon);
Packit Service ed5168
            this.actor.add_child(label);
Packit Service ed5168
        }
Packit Service ed5168
Packit Service ed5168
        open() {
Packit Service ed5168
            log("menu opened!");
Packit Service ed5168
        }
Packit Service ed5168
    };
Packit Service ed5168
```
Packit Service ed5168
Packit Service ed5168
For GObject inheritence, we use the GObject.registerClass() function provided
Packit Service ed5168
by gjs.
Packit Service ed5168
```javascript
Packit Service ed5168
    var MyActor = GObject.registerClass(
Packit Service ed5168
    class MyActor extends Clutter.Actor {
Packit Service ed5168
        _init(params) {
Packit Service ed5168
            super._init(params);
Packit Service ed5168
Packit Service ed5168
            this.name = 'MyCustomActor';
Packit Service ed5168
        }
Packit Service ed5168
    });
Packit Service ed5168
```
Packit Service ed5168
Packit Service ed5168
## GObject Introspection
Packit Service ed5168
Packit Service ed5168
GObject Introspection is a powerful feature that allows us to have native
Packit Service ed5168
bindings for almost any library built around GObject. If a library requires
Packit Service ed5168
you to inherit from a type to use it, you can do so:
Packit Service ed5168
```javascript
Packit Service ed5168
    var MyClutterActor = GObject.registerClass(
Packit Service ed5168
    class MyClutterActor extends Clutter.Actor {
Packit Service ed5168
Packit Service ed5168
        vfunc_get_preferred_width(forHeight) {
Packit Service ed5168
             return [100, 100];
Packit Service ed5168
        }
Packit Service ed5168
Packit Service ed5168
        vfunc_get_preferred_height(forWidth) {
Packit Service ed5168
             return [100, 100];
Packit Service ed5168
        }
Packit Service ed5168
Packit Service ed5168
        vfunc_paint() {
Packit Service ed5168
             let alloc = this.get_allocation_box();
Packit Service ed5168
             Cogl.set_source_color4ub(255, 0, 0, 255);
Packit Service ed5168
             Cogl.rectangle(alloc.x1, alloc.y1,
Packit Service ed5168
                            alloc.x2, alloc.y2);
Packit Service ed5168
        }
Packit Service ed5168
    });
Packit Service ed5168
```
Packit Service ed5168
Packit Service ed5168
## Translatable strings, `environment.js`
Packit Service ed5168
Packit Service ed5168
We use gettext to translate the GNOME Shell into all the languages that GNOME
Packit Service ed5168
supports. The `gettext` function is aliased globally as `_`, you do not need to
Packit Service ed5168
explicitly import it. This is done through some magic in the
Packit Service ed5168
[environment.js](http://git.gnome.org/browse/gnome-shell/tree/js/ui/environment.js)
Packit Service ed5168
file. If you can't find a method that's used, it's probably either in gjs itself
Packit Service ed5168
or installed on the global object from the Environment.
Packit Service ed5168
Packit Service ed5168
Use 'single quotes' for programming strings that should not be translated
Packit Service ed5168
and "double quotes" for strings that the user may see. This allows us to
Packit Service ed5168
quickly find untranslated or mistranslated strings by grepping through the
Packit Service ed5168
sources for double quotes without a gettext call around them.
Packit Service ed5168
Packit Service ed5168
## `actor` and `_delegate`
Packit Service ed5168
Packit Service ed5168
gjs allows us to set so-called "expando properties" on introspected objects,
Packit Service ed5168
allowing us to treat them like any other. Because the Shell was built before
Packit Service ed5168
you could inherit from GTypes natively in JS, we usually have a wrapper class
Packit Service ed5168
that has a property called `actor`. We call this wrapper class the "delegate".
Packit Service ed5168
Packit Service ed5168
We sometimes use expando properties to set a property called `_delegate` on
Packit Service ed5168
the actor itself:
Packit Service ed5168
```javascript
Packit Service ed5168
    var MyClass = class {
Packit Service ed5168
        constructor() {
Packit Service ed5168
            this.actor = new St.Button({ text: "This is a button" });
Packit Service ed5168
            this.actor._delegate = this;
Packit Service ed5168
Packit Service ed5168
            this.actor.connect('clicked', this._onClicked.bind(this));
Packit Service ed5168
        }
Packit Service ed5168
Packit Service ed5168
        _onClicked(actor) {
Packit Service ed5168
            actor.set_label("You clicked the button!");
Packit Service ed5168
        }
Packit Service ed5168
    };
Packit Service ed5168
```
Packit Service ed5168
Packit Service ed5168
The 'delegate' property is important for anything which trying to get the
Packit Service ed5168
delegate object from an associated actor. For instance, the drag and drop
Packit Service ed5168
system calls the `handleDragOver` function on the delegate of a "drop target"
Packit Service ed5168
when the user drags an item over it. If you do not set the `_delegate`
Packit Service ed5168
property, your actor will not be able to be dropped onto.
Packit Service ed5168
Packit Service ed5168
## Functional style
Packit Service ed5168
Packit Service ed5168
JavaScript Array objects offer a lot of common functional programming
Packit Service ed5168
capabilities such as forEach, map, filter and so on. You can use these when
Packit Service ed5168
they make sense, but please don't have a spaghetti mess of function programming
Packit Service ed5168
messed in a procedural style. Use your best judgment.
Packit Service ed5168
Packit Service ed5168
## Closures
Packit Service ed5168
Packit Service ed5168
`this` will not be captured in a closure, it is relative to how the closure is
Packit Service ed5168
invoked, not to the value of this where the closure is created, because "this"
Packit Service ed5168
is a keyword with a value passed in at function invocation time, it is not a
Packit Service ed5168
variable that can be captured in closures.
Packit Service ed5168
Packit Service ed5168
All closures should be wrapped with Function.prototype.bind or use arrow
Packit Service ed5168
notation.
Packit Service ed5168
```javascript
Packit Service ed5168
    let closure1 = () => { this._fnorbate(); };
Packit Service ed5168
    let closure2 = this._fnorbate.bind(this);
Packit Service ed5168
```
Packit Service ed5168
Packit Service ed5168
A more realistic example would be connecting to a signal on a method of a
Packit Service ed5168
prototype:
Packit Service ed5168
```javascript
Packit Service ed5168
    const FnorbLib = imports.fborbLib;
Packit Service ed5168
Packit Service ed5168
    var MyClass = class {
Packit Service ed5168
        _init() {
Packit Service ed5168
            let fnorb = new FnorbLib.Fnorb();
Packit Service ed5168
            fnorb.connect('frobate', this._onFnorbFrobate.bind(this));
Packit Service ed5168
        }
Packit Service ed5168
Packit Service ed5168
        _onFnorbFrobate(fnorb) {
Packit Service ed5168
            this._updateFnorb();
Packit Service ed5168
        }
Packit Service ed5168
    };
Packit Service ed5168
```
Packit Service ed5168
Packit Service ed5168
## Object literal syntax
Packit Service ed5168
Packit Service ed5168
In JavaScript, these are equivalent:
Packit Service ed5168
```javascript
Packit Service ed5168
    foo = { 'bar': 42 };
Packit Service ed5168
    foo = { bar: 42 };
Packit Service ed5168
```
Packit Service ed5168
Packit Service ed5168
and so are these:
Packit Service ed5168
```javascript
Packit Service ed5168
    var b = foo['bar'];
Packit Service ed5168
    var b = foo.bar;
Packit Service ed5168
```
Packit Service ed5168
Packit Service ed5168
If your usage of an object is like an object, then you're defining "member
Packit Service ed5168
variables." For member variables, use the no-quotes no-brackets syntax: `{ bar:
Packit Service ed5168
42 }` `foo.bar`.
Packit Service ed5168
Packit Service ed5168
If your usage of an object is like a hash table (and thus conceptually the keys
Packit Service ed5168
can have special chars in them), don't use quotes, but use brackets: `{ bar: 42
Packit Service ed5168
}`, `foo['bar']`.
Packit Service ed5168
Packit Service ed5168
## Getters, setters, and Tweener
Packit Service ed5168
Packit Service ed5168
Getters and setters should be used when you are dealing with an API that is
Packit Service ed5168
designed around setting properties, like Tweener. If you want to animate an
Packit Service ed5168
arbitrary property, create a getter and setter, and use Tweener to animate the
Packit Service ed5168
property.
Packit Service ed5168
```javascript
Packit Service ed5168
    var ANIMATION_TIME = 2000;
Packit Service ed5168
Packit Service ed5168
    var MyClass = class {
Packit Service ed5168
        constructor() {
Packit Service ed5168
            this.actor = new St.BoxLayout();
Packit Service ed5168
            this._position = 0;
Packit Service ed5168
        }
Packit Service ed5168
Packit Service ed5168
        get position() {
Packit Service ed5168
            return this._position;
Packit Service ed5168
        }
Packit Service ed5168
Packit Service ed5168
        set position(value) {
Packit Service ed5168
            this._position = value;
Packit Service ed5168
            this.actor.set_position(value, value);
Packit Service ed5168
        }
Packit Service ed5168
    };
Packit Service ed5168
Packit Service ed5168
    let myThing = new MyClass();
Packit Service ed5168
    Tweener.addTween(myThing,
Packit Service ed5168
                     { position: 100,
Packit Service ed5168
                       time: ANIMATION_TIME,
Packit Service ed5168
                       transition: 'easeOutQuad' });
Packit Service ed5168
```