PageRenderTime 35ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/HACKING.md

https://gitlab.com/tchaik/gnome-shell
Markdown | 327 lines | 255 code | 72 blank | 0 comment | 0 complexity | aa68110275f6611ad61f467b398a9288 MD5 | raw file
  1. # Coding guide
  2. Our goal is to have all JavaScript code in GNOME follow a consistent style. In
  3. a dynamic language like JavaScript, it is essential to be rigorous about style
  4. (and unit tests), or you rapidly end up with a spaghetti-code mess.
  5. ## A quick note
  6. Life isn't fun if you can't break the rules. If a rule seems unnecessarily
  7. restrictive while you're coding, ignore it, and let the patch reviewer decide
  8. what to do.
  9. ## Indentation, braces and whitespace
  10. * Use four-space indents.
  11. * Braces are on the same line as their associated statements.
  12. * You should only omit braces if *both* sides of the statement are on one line.
  13. * One space after the `function` keyword.
  14. * No space between the function name in a declaration or a call.
  15. * One space before the parens in the `if` statements, or `while`, or `for` loops.
  16. ```javascript
  17. function foo(a, b) {
  18. let bar;
  19. if (a > b)
  20. bar = do_thing(a);
  21. else
  22. bar = do_thing(b);
  23. if (var == 5) {
  24. for (let i = 0; i < 10; i++) {
  25. print(i);
  26. }
  27. } else {
  28. print(20);
  29. }
  30. }
  31. ```
  32. ## Semicolons
  33. JavaScript allows omitting semicolons at the end of lines, but don't. Always
  34. end statements with a semicolon.
  35. ## js2-mode
  36. If using Emacs, do not use js2-mode. It is outdated and hasn't worked for a
  37. while. emacs now has a built-in JavaScript mode, js-mode, based on
  38. espresso-mode. It is the de facto emacs mode for JavaScript.
  39. ## File naming and creation
  40. For JavaScript files, use lowerCamelCase-style names, with a `.js` extension.
  41. We only use C where gjs/gobject-introspection is not available for the task, or
  42. where C would be cleaner. To work around limitations in
  43. gjs/gobject-introspection itself, add a new method in `shell-util.[ch]`.
  44. Like many other GNOME projects, we prefix our C source filenames with the
  45. library name followed by a dash, e.g. `shell-app-system.c`. Create a
  46. `-private.h` header when you want to share code internally in the
  47. library. These headers are not installed, distributed or introspected.
  48. ## Imports
  49. Use UpperCamelCase when importing modules to distinguish them from ordinary
  50. variables, e.g.
  51. ```javascript
  52. const GLib = imports.gi.GLib;
  53. ```
  54. Imports should be categorized into one of two places. The top-most import block
  55. should contain only "environment imports". These are either modules from
  56. gobject-introspection or modules added by gjs itself.
  57. The second block of imports should contain only "application imports". These
  58. are the JS code that is in the gnome-shell codebase,
  59. e.g. `imports.ui.popupMenu`.
  60. Each import block should be sorted alphabetically. Don't import modules you
  61. don't use.
  62. ```javascript
  63. const GLib = imports.gi.GLib;
  64. const Gio = imports.gi.Gio;
  65. const Lang = imports.lang;
  66. const St = imports.gi.St;
  67. const Main = imports.ui.main;
  68. const Params = imports.misc.params;
  69. const Tweener = imports.ui.tweener;
  70. const Util = imports.misc.util;
  71. ```
  72. The alphabetical ordering should be done independently of the location of the
  73. location. Never reference `imports` in actual code.
  74. ## Constants
  75. We use CONSTANTS_CASE to define constants. All constants should be directly
  76. under the imports:
  77. ```javascript
  78. const MY_DBUS_INTERFACE = 'org.my.Interface';
  79. ```
  80. ## Variable declaration
  81. Always use either `const` or `let` when defining a variable.
  82. ```javascript
  83. // Iterating over an array
  84. for (let i = 0; i < arr.length; ++i) {
  85. let item = arr[i];
  86. }
  87. // Iterating over an object's properties
  88. for (let prop in someobj) {
  89. ...
  90. }
  91. ```
  92. If you use "var" then the variable is added to function scope, not block scope.
  93. 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)
  94. ## Classes
  95. There are many approaches to classes in JavaScript. We use our own class framework
  96. (sigh), which is built in gjs. The advantage is that it supports inheriting from
  97. GObjects, although this feature isn't used very often in the Shell itself.
  98. ```javascript
  99. var IconLabelMenuItem = new Lang.Class({
  100. Name: 'IconLabelMenuItem',
  101. Extends: PopupMenu.PopupMenuBaseItem,
  102. _init(icon, label) {
  103. this.parent({ reactive: false });
  104. this.actor.add_child(icon);
  105. this.actor.add_child(label);
  106. },
  107. open() {
  108. log("menu opened!");
  109. }
  110. });
  111. ```
  112. * 'Name' is required. 'Extends' is optional. If you leave it out, you will
  113. automatically inherit from Object.
  114. * Leave a blank line between the "class header" (Name, Extends, and other
  115. things) and the "class body" (methods). Leave a blank line between each
  116. method.
  117. * No space before the colon, one space after.
  118. * No trailing comma after the last item.
  119. * Make sure to use a semicolon after the closing paren to the class. It's
  120. still a giant function call, even though it may resemble a more
  121. conventional syntax.
  122. ## GObject Introspection
  123. GObject Introspection is a powerful feature that allows us to have native
  124. bindings for almost any library built around GObject. If a library requires
  125. you to inherit from a type to use it, you can do so:
  126. ```javascript
  127. var MyClutterActor = new Lang.Class({
  128. Name: 'MyClutterActor',
  129. Extends: Clutter.Actor,
  130. vfunc_get_preferred_width(actor, forHeight) {
  131. return [100, 100];
  132. },
  133. vfunc_get_preferred_height(actor, forWidth) {
  134. return [100, 100];
  135. },
  136. vfunc_paint(actor) {
  137. let alloc = this.get_allocation_box();
  138. Cogl.set_source_color4ub(255, 0, 0, 255);
  139. Cogl.rectangle(alloc.x1, alloc.y1,
  140. alloc.x2, alloc.y2);
  141. }
  142. });
  143. ```
  144. ## Translatable strings, `environment.js`
  145. We use gettext to translate the GNOME Shell into all the languages that GNOME
  146. supports. The `gettext` function is aliased globally as `_`, you do not need to
  147. explicitly import it. This is done through some magic in the
  148. [environment.js](http://git.gnome.org/browse/gnome-shell/tree/js/ui/environment.js)
  149. file. If you can't find a method that's used, it's probably either in gjs itself
  150. or installed on the global object from the Environment.
  151. Use 'single quotes' for programming strings that should not be translated
  152. and "double quotes" for strings that the user may see. This allows us to
  153. quickly find untranslated or mistranslated strings by grepping through the
  154. sources for double quotes without a gettext call around them.
  155. ## `actor` and `_delegate`
  156. gjs allows us to set so-called "expando properties" on introspected objects,
  157. allowing us to treat them like any other. Because the Shell was built before
  158. you could inherit from GTypes natively in JS, we usually have a wrapper class
  159. that has a property called `actor`. We call this wrapper class the "delegate".
  160. We sometimes use expando properties to set a property called `_delegate` on
  161. the actor itself:
  162. ```javascript
  163. var MyClass = new Lang.Class({
  164. Name: 'MyClass',
  165. _init() {
  166. this.actor = new St.Button({ text: "This is a button" });
  167. this.actor._delegate = this;
  168. this.actor.connect('clicked', this._onClicked.bind(this));
  169. },
  170. _onClicked(actor) {
  171. actor.set_label("You clicked the button!");
  172. }
  173. });
  174. ```
  175. The 'delegate' property is important for anything which trying to get the
  176. delegate object from an associated actor. For instance, the drag and drop
  177. system calls the `handleDragOver` function on the delegate of a "drop target"
  178. when the user drags an item over it. If you do not set the `_delegate`
  179. property, your actor will not be able to be dropped onto.
  180. ## Functional style
  181. JavaScript Array objects offer a lot of common functional programming
  182. capabilities such as forEach, map, filter and so on. You can use these when
  183. they make sense, but please don't have a spaghetti mess of function programming
  184. messed in a procedural style. Use your best judgment.
  185. ## Closures
  186. `this` will not be captured in a closure, it is relative to how the closure is
  187. invoked, not to the value of this where the closure is created, because "this"
  188. is a keyword with a value passed in at function invocation time, it is not a
  189. variable that can be captured in closures.
  190. All closures should be wrapped with Function.prototype.bind or use arrow
  191. notation.
  192. ```javascript
  193. const Lang = imports.lang;
  194. let closure1 = () => { this._fnorbate(); };
  195. let closure2 = this._fnorbate.bind(this);
  196. ```
  197. A more realistic example would be connecting to a signal on a method of a
  198. prototype:
  199. ```javascript
  200. const Lang = imports.lang;
  201. const FnorbLib = imports.fborbLib;
  202. var MyClass = new Lang.Class({
  203. _init() {
  204. let fnorb = new FnorbLib.Fnorb();
  205. fnorb.connect('frobate', this._onFnorbFrobate.bind(this));
  206. },
  207. _onFnorbFrobate(fnorb) {
  208. this._updateFnorb();
  209. }
  210. });
  211. ```
  212. ## Object literal syntax
  213. In JavaScript, these are equivalent:
  214. ```javascript
  215. foo = { 'bar': 42 };
  216. foo = { bar: 42 };
  217. ```
  218. and so are these:
  219. ```javascript
  220. var b = foo['bar'];
  221. var b = foo.bar;
  222. ```
  223. If your usage of an object is like an object, then you're defining "member
  224. variables." For member variables, use the no-quotes no-brackets syntax: `{ bar:
  225. 42 }` `foo.bar`.
  226. If your usage of an object is like a hash table (and thus conceptually the keys
  227. can have special chars in them), don't use quotes, but use brackets: `{ bar: 42
  228. }`, `foo['bar']`.
  229. ## Getters, setters, and Tweener
  230. Getters and setters should be used when you are dealing with an API that is
  231. designed around setting properties, like Tweener. If you want to animate an
  232. arbitrary property, create a getter and setter, and use Tweener to animate the
  233. property.
  234. ```javascript
  235. var ANIMATION_TIME = 2000;
  236. var MyClass = new Lang.Class({
  237. Name: 'MyClass',
  238. _init() {
  239. this.actor = new St.BoxLayout();
  240. this._position = 0;
  241. },
  242. get position() {
  243. return this._position;
  244. },
  245. set position(value) {
  246. this._position = value;
  247. this.actor.set_position(value, value);
  248. }
  249. });
  250. let myThing = new MyClass();
  251. Tweener.addTween(myThing,
  252. { position: 100,
  253. time: ANIMATION_TIME,
  254. transition: 'easeOutQuad' });
  255. ```