Prerequisite knowledge

A basic understanding of JavaScript programming and code editors.

Additional Required product


User level


Brackets, the new code editor for the web, is a fairly ambitious project on many levels, not the least of which is that the tool itself is written using JavaScript, CSS, and HTML, with the developers using Brackets itself when possible.
At a time when discussions around best practices for web app development have never been so lively, Brackets, which is open source, is a great opportunity for developers to witness how a complex, real-life project based on web standards can be built. While the code editor itself is still in its infancy (Sprint 11 just released at the time of this writing), there are already many things you can learn from studying its current source code.

General organization

Brackets is hosted on GitHub, within two different repositories: one for the main source code responsible for the core web application; the other one containing the source of the native application shell, which makes it possible for the editor to run as a stand-alone application for Windows and Mac OS (and before you ask: yes, Linux is on the roadmap). For this native application shell, Brackets uses the Chromium Embedded Framework, which embeds a version of WebKit.
The reason for this separation of concerns might be obvious: Brackets has the potential to be run in many places outside native desktop apps. This ubiquity is the main strength of web standards, and it's what makes the project so exciting, if you ask me.
In terms of file organization, the core Brackets repository has a pretty self-explanatory folder structure, and is rather well commented, so I think there is no need to detail each and every one of them sequentially here. Instead, I'd rather highlight the various aspects that struck me as a developer.

Main code conventions

A quick look at any JavaScript file in the repository will answer a common question regarding modern web apps: Yes, Brackets JavaScript code is written using modules. This should be no surprise. Modules are a great way to provide a clear, limited scope and a proper encapsulation of your JavaScript code by taking advantage of closures. It's also a great way to provide a basic level of privacy, since only the members exposed via the returned object are publicly available.
As you may know, there are several format proposals for modules, but only two are really used today: CommonJS Modules and AMD (for Asynchronous Module Definition). The Brackets team has chosen a hybrid approach: it uses the CommonJS format inside AMD-compatible wrappers:
define(function (require, exports, module) { var otherModule = require("path/to/required/Module"); var _privateStuff="I'm private"; var publicStuff="I need to be accessed from outside"; function doStuff(){ console.log(publicStuff); } // Exposing public members through the exports object exports.doStuff=doStuff; exports.publicStuff= publicStuff; });
Note that the module parameter is not used: it's only there for CommonJS compliance. Also, modules are loaded via the popular script/module loader RequireJS. For more information on modules and module loaders, I highly recommend reading Addy Osmani's Writing Modular JavaScript. By the way, an important thing to keep in mind is that a module, and its corresponding file, may define zero, one, or many classes. So if you're looking for a class definition, don't necessary look for the corresponding file name.
Another interesting bit you might encounter while wandering around the repository is the use of a deferred/promise mechanism. To put it shortly, it's a callback mechanism in which a so-called deferred object can register and queue multiple callbacks and call them according to its state (pending, success, or failure, typically). This pattern is used to handle asynchronous APIs, which are needed if only because, in a browser-hosted version of the editor, all I/O operations would be asynchronous. To learn more about this pattern, I'd recommend reading the Deferred Object entry in the jQuery docs or the CommonJS Promises/A proposal it is built upon.
In case you're wondering, Brackets does not use any third-party micro-architecture framework. You'll find some common concepts such as models, views, and controller, but not formalized around a particular mechanism.
Finally, there is a page on the wiki describing code conventions used for Brackets. While nothing I saw should surprise any JavaScript developer, I still think it's important for such an open project to agree and communicate about those practices, even if they're quite common.

Code editing and document management

Much of what makes Brackets a great tool for actually editing code comes from the use of a great third-party project: CodeMirror. Everything regarding laying out a text document, to representing code and editing it in a proper way, comes from CodeMirror, including syntax highlighting and focus management.
Document and project management, however, is handled separately. This is the logic responsible for things like defining a project or a working set, selecting a file, and keeping track of open files inside that project. While this may seem trivial when put this way, this is actually a very central part of any software designed to edit documents.
Several classes are involved in this process, such as Document, Editor, and their respective managers, and understanding their relationship is essential to understanding the Brackets architecture.
While a Document represents a file and exposes methods to edit it programmatically, an Editor encapsulates the UI for the user to edit the document. Documents and Editors have a one-to-many relationship (one Document may be edited by several Editors). You can see the Document as a model and the Editor as a middleman between a view and the Document object it represents.
The DocumentManager and the EditorManager classes create, delete, and provide a central access for all documents and editors, respectively. For instance, to access the current document, you can ask the DocumentManager:
var currentDoc = DocumentManager.getCurrentDocument();
But since Editors keep track of the document they edit, and the EditorManager manages all editors, you could also go this way:
var currentDoc = EditorManager.getFocusedEditor().document;
Note that the current Editor and its corresponding Document do not necessary correspond to the currently opened tab in the UI, because Brackets has a notion of inline editors which can come on top of the main, full size editor representing the currently opened document.
Editors abstract the CodeMirror implementation, providing methods to get and set the selection, cursor position, scroll position, and other properties. The underlying CodeMirror API can still be accessed directly through _codeMirror , but you should use Document or Editor methods whenever possible.
The Document API exposes text editing operations, such as setText() and replaceRange() , which are automatically reflected by all Editors associated with that Document. So, to modify text, simply use the Document's API, regardless of Editors:
currentDoc.replaceRange("Hello world", startPosition, endPosition);
Selection management, however, is related to a corresponding view, so it's only available through the Editor object, using methods like getSelectedText() and setSelection(start, end) . Here's how you could set the selection of the document that currently has focus:
EditorManager.getFocusedEditor().setSelection({line:0, ch:0}, {line:0, ch:5});
Finally, a word of warning: As a workaround to circumvent the lack of weak references in JavaScript, if, when you need to keep a reference of a Document object, you have to call Document.addRef() and, conversely, Document.releaseRef() when you're done. This includes all event listeners attached to the document.
To learn more about Documents in Brackets, I highly recommend reading the dedicated section in its github wiki.

Live development and inline editors

One of things that makes Brackets unique today is its live development feature: Brackets is "connected" to the browser in such a way that modifications while you type are immediately shown in the browser. Needless to say, this is potentially a killer feature in terms of productivity.
At the heart of the implementation of live development is the Inspector.js file, which is in charge of the communication with Chrome's remote debugger API using JSON over WebSockets. Other elements involved in this process include Agents—which keep track of changes and propagate them—and Documents which, again, represent CSS, JS, and HTML files on the system (not to be confused with the generic Document class described above).
The inline editor feature, which makes it possible to overlay some content over code, is implemented through the InlineWidget class (the very basis of inline editors, which is just a panel floating over your code). The InlineTextEditor class is slightly more sophisticated: it inherits from InlineWidget and adds a simple text editor.
The class you'd use to create an inline editor such as Brackets' default CSS inline editor, is the MultiRangeInlineEditor, which itself inherits from InlineTextEditor.
To use such an inline editor, you have to register a provider function with the EditorManager, like this:
This function takes two parameters: the host editor and a position object. Position objects are used by CodeMirror and contain two properties: the line number and the character position. Inside this function, you'll simply instantiate your MultiRangeInlineEditor and pass it the corresponding Editor instance, returning a Promise object (see above) that is resolved once this is complete:

Views and user input management

function providerFunction(hostEditor, pos){ //Create an instance of InlineTextEditor or one of its subclasses var myInlineEditor = new MyMultiRangeInlineEditor(data); myInlineEditor.load(hostEditor); //Only handle cases where this inlineEditor makes sense // and leave the place to other editors for all other cases if(notRelevantContext) return null //Assume this will be asynchronous var result = new $.Deferred(); result.resolve(myInlineEditor); return result.promise(); }
I will not dive that much into styling, widgets, layout, and other UI details here, but I wanted to point out that, besides jQuery, Brackets makes use of the über-popular Twitter Bootstrap framework for its elegant UI which means it also uses the LESS CSS preprocessor.
Brackets does not use any templating system. Most of the UI is hard-coded in the main Brackets HTML file. My understanding is that this may change in the future.
More important to understand is how Brackets handles user input. To do so, it makes use of the Command pattern. This pattern encapsulates actions to be performed by the software (e.g. "open file") as objects (the Command). It is very convenient to map user gestures and inputs to those objects, for many reasons:
  1. It allows the decoupling of user interactions from its consequence in the editor, which yields more maintainable code.
  2. Since actions are treated like objects, they can hold state, and you can perform all sorts of operations on them. For instance, if you disable a Command, or change its label, its corresponding UI elements will be immediately updated. This is also a popular pattern for implementing the basics of things such as history management and tool scripting, so you can expect those features in the future.
  3. It makes the code much easier to to test.
In terms of implementation, registering a Command is a simple as that:
CommandManager.register(label, commandID, callback);
You can then attach it to a menu item so that it is triggered when this item is selected by the user:
var menu = Menus.getMenu(Menus.AppMenuBar.FILE_MENU); menu.addMenuItem(commandID);
... but you can also trigger it manually:


Code quality is absolutely essential for any serious project, and of course Brackets is no exception. It uses JSLint to evaluate the quality of its code. JSLint is one of the most popular JavaScript static analysis tools. It was made by Douglas Crockford as a way to scan your code easily and report any trouble, as described by its comment-based configuration mechanism.
You'll find JSLint configuration directives on top of every module:
/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, forin: true, maxerr: 50, regexp: true */ /*global define, $, FileError, brackets, window */
Note that JSLint is also used by default in Brackets itself for providing developers an immediate feedback on the quality of their code.
Brackets also uses a testing framework, Jasmine, to better guarantee the quality of the code. Jasmine is a popular Behavior-driven development (a flavor of Test-driven development) framework used for JavaScript development.
A test runner has been included in the editor itself (under Debug > Run Tests), so that it can easily be tested...from itself.

Early extensibility model

An early extensibility mechanism is already included, enabling users to extend Brackets functionality easily without having to patch the core code. The typical workflow for doing so would be to launch a second instance of Brackets for testing the actual code, using the Debug > New Window command.
The extension itself is of course described as a module, but in a somewhat separate scope from the rest of the source code. To load dependencies from Brackets, you have to use brackets.getModule() .
Here's a complete Hello World extension originally written by Mike Chambers.
/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */ /*global define, $, brackets, window */ /** Simple extension that adds a "File > Hello World" menu item */ define(function (require, exports, module) { 'use strict'; var CommandManager = brackets.getModule("command/CommandManager"), Menus = brackets.getModule("command/Menus"); // Function to run when the menu item is clicked function handleHelloWorld() { window.alert("Hello, world!"); } // First, register a command - a UI-less object associating an id to a handler var MY_COMMAND_ID = "helloworld.sayhello"; CommandManager.register("Hello World", MY_COMMAND_ID, handleHelloWorld); // Then create a menu item bound to the command // The label of the menu item is the name we gave the command (see above) var menu = Menus.getMenu(Menus.AppMenuBar.FILE_MENU); menu.addMenuItem(MY_COMMAND_ID); // We could also add a key binding at the same time: //menu.addMenuItem(MY_COMMAND_ID, "Ctrl-Alt-H"); // (Note: "Ctrl" is automatically mapped to "Cmd" on Mac) // Or you can add a key binding without having to create a menu item: //KeyBindingManager.addBinding(MY_COMMAND_ID, "Ctrl-Alt-H"); // For dynamic menu item labels, you can change the command name at any time: //CommandManager.get(MY_COMMAND_ID).setName("Goodbye!"); });
What's great is that your extension can take advantage of pretty much all of Brackets features, like the inline editor mechanism.

Where to go from here

I hope that this somewhat high-level overview will make you want to know more about this exciting project, and even potentially contribute to Brackets.
For a better, deeper understanding of how Brackets works, I definitely recommend taking a look at the Brackets wiki. There are also various videos on YouTube from the first Brackets hackathon, but some things described here are really out of date, so take it with a grain of salt.