Wednesday, February 12, 2014

Ploughing through the file structure

First question I asked myself was: What is the entry point?
Logical answer for a project written in html, css and JavaScript: index.html

So let's have a look at this index file... Don't worry, I won't display the source code of every file; just the few files that form the bootstrap of Brackets.

index.html

<html>
<head>
    <meta charset="utf-8">
    <title></title>

    <!-- CSS/LESS -->

    <!-- NOTE: All scripts must be external for Chrome App support: http://developer.chrome.com/apps/app_csp.html -->

    <!-- Warn about failed cross origin requests in Chrome -->
    <script type="application/javascript" src="xorigin.js"></script>

    <link rel="stylesheet" type="text/css" href="thirdparty/CodeMirror2/lib/codemirror.css">
    <!--(if target dev)><!-->
    <link rel="stylesheet" type="text/less" href="styles/brackets.less">
    <!--<!(endif)-->
    <!--(if target dist)>
    <link rel="stylesheet" type="text/css" href="styles/brackets.min.css">
    <!--<!(endif)-->
    
    <!-- JavaScript -->

    <!-- Pre-load third party scripts that cannot be async loaded. -->
    <!-- build:js thirdparty/thirdparty.min.js -->
    <script src="thirdparty/less-1.4.2.min.js"></script>
    <script src="thirdparty/mustache/mustache.js"></script>
    <script src="thirdparty/jquery-2.0.1.min.js"></script>
    <script src="thirdparty/CodeMirror2/lib/codemirror.js"></script>
    <script src="thirdparty/CodeMirror2/addon/fold/xml-fold.js"></script>
    <script src="thirdparty/CodeMirror2/addon/edit/matchtags.js"></script>
    <script src="thirdparty/CodeMirror2/addon/edit/matchbrackets.js"></script>
    <script src="thirdparty/CodeMirror2/addon/edit/closebrackets.js"></script>
    <script src="thirdparty/CodeMirror2/addon/edit/closetag.js"></script>
    <script src="thirdparty/CodeMirror2/addon/selection/active-line.js"></script>
    
    <!-- JS for CodeMirror multiplex and overlay mode support -->
    <script src="thirdparty/CodeMirror2/addon/mode/multiplex.js"></script>
    <script src="thirdparty/CodeMirror2/addon/mode/overlay.js"></script>

    <!-- JS for CodeMirror search support -->
    <script src="thirdparty/CodeMirror2/addon/search/searchcursor.js"></script>
    <!-- endbuild -->

</head>
<body>

    <!-- HTML content is dynamically loaded and rendered by brackets.js.
         Any modules that depend on or modify HTML during load should
         require the "utils/AppInit" module and install a callback for
         "htmlReady" (e.g. AppInit.htmlReady(handler)) before touching the DOM.
    -->
    
    <!-- All other scripts are loaded through require. -->
    <!-- build:js main.js -->
    <script src="thirdparty/requirejs/require.js" data-main="main"></script>
    <!-- endbuild -->

    <!-- Verify that all dependencies are loaded. -->
    <script src="dependencies.js"></script>

</body>
</html>

Hey! This is a normal html file! So what would happen if I point my browser to it?

Nope... No joy there. Someday you will be able to, but for now it runs inside the Brackets-shell which is also an open source project. You will need to dig a bit deeper into different languages though to get busy contributing there. According to github it consists of 59.2% Python, 22% C++, 9.0% Objective-C, 7.4% JavaScript, 1.6% C and 0.6% Shell.

Anyway, back to the task at hand and going straight to the core;
Line 55 is where the magic happens. RequireJS is being included and we tell it to load a main.js file in the same folder as the index page; that is where the bootstrap starts.

So we can now have a look at the main.js file.

main.js

require.config({
    paths: {
        "text"              : "thirdparty/text/text",
        "i18n"              : "thirdparty/i18n/i18n",
        
        // The file system implementation. Change this value to use different 
        // implementations (e.g. cloud-based storage).
        "fileSystemImpl"    : "filesystem/impls/appshell/AppshellFileSystem"
    }
});

// hack for r.js optimization, move locale to another config call

// Use custom brackets property until CEF sets the correct navigator.language
// NOTE: When we change to navigator.language here, we also should change to
// navigator.language in ExtensionLoader (when making require contexts for each
// extension).
require.config({
    locale: window.localStorage.getItem("locale") || (typeof (brackets) !== "undefined" ? brackets.app.language : navigator.language)
});

define(function (require, exports, module) {
    "use strict";
    
    // Load the brackets module. This is a self-running module that loads and runs the entire application.
    require("brackets");
});

Not too much to work through now is it? We can see it is setting up some configurations to load, mostly to do with localisation and the file system. Finally on line 26, the brackets module is being loaded. The file that makes up this module is brackets.js and it is located in the same folder as the index page.

The file is close to 400 lines so I won't display it here, but it is quite straight forward, the comment explains what the module does.

brackets.js

/**
 * brackets is the root of the Brackets codebase. This file pulls in all other modules as
 * dependencies (or dependencies thereof), initializes the UI, and binds global menus & keyboard
 * shortcuts to their Commands.
 *
 * Unlike other modules, this one can be accessed without an explicit require() because it exposes
 * a global object, window.brackets.
 */

So there we have it, the complete startup process. From there it should not be too difficult to get started hacking away, creating features, bug fixes.

Tuesday, February 11, 2014

Love the Brackets editor

While searching for a nice JavaScript editor, a colleague pointed me towards brackets.io; Adobe's first full blown open source project!
Although I have never been an Adobe 'fan-boy' I do concede to the fact that they have done a lot for the web in years long past... and they do have some pretty awesome software.

Why Brackets

Now they have finally embraced the open source community for this little gem of a project! I would almost say it was love at first sight! Three features that jumped right out of the proverbial box:
  1. Inline editor that eliminates the need to jump from file to file.
  2. Written in JavaScript, html and css so no need to tap into different languages to create your own cool extensions.
  3. Built in test suite for when you are hacking or building extensions.

Testing Code

As for testing user code; there is talk about extending the built in test for this purpose, but while we wait for that there are some pretty cool extensions out there that we can use in the mean time:
  1. brackets-xunit to create all sorts of tests using different systems.
  2. karma-brackets to run tests inside a docked window within brackets.

Contribute to Brackets

Anyway I can't wait to start hacking and that is exactly the reason for starting this blog! I will try to document my journey on how to get to know the brackets architecture and source code so that it may help others to find their way.

Following the instructions from the brackets.io website:
  1. Explore the repo to get an idea of the source.
  2. Set up a DEV environment using the repo.
From an introductory video I learned that the Adobe team decided to group some of the issues reported on the github repository as 'starter bugs'. Those could help a starting hacker to get to know the code by solving easy bugs. Awesome idea, but in practice it does not quite work as expected.
In all reality, at the time of this writing there are 15 open starter bugs, most of which have been open for quite some time, some have already been fixed by the Adobe developers on their local code, others are actually pebkac issues where users can't follow instructions. All in all there really isn't that much to choose from.

Brackets Architecture

Searching for different resources...
As described in the first link, one soon finds out that Brackets is written using modules in a CommenJS format inside AMD-compatible wrappers and uses RequireJS to load these modules. David Deraedt recommends all who want more information on modules and module loading to checkout Addy Osmani's blog on Writing Modular JavaScript.

David continues to point out another interesting aspect about the Brackets source code; there is a queue system to handle asynchronous requests using a deferred-promise mechanism. recommended reads are jQuery - Deferred Objects and CommonJS - Promises/A proposal.

Taking it all in

That is a lot too take in, all in one go. Tuns of information...
I know one thing though, this will make me a better JavaScript coder altogether!

Time to dig in!