How to Hack on Brackets

Narciso (NJ) Jaramillo

What To Hack On

What Should I Work On?

Or whatever you want!

Signing Up for a Bug

  • Check the labels
  • Comment on the bug to claim it
  • Submit a pull request
  • After it's merged, add a comment asking filer to close

Signing Up for a Feature

  • Mention what you're working on on the Google Group
  • Describe your design/approach and get feedback
  • Or post a prototype (as a branch or an extension)

Hacking on Core

Get Set Up

  • Install a build of Brackets to get the shell
  • Fork the Brackets repo
  • Run the setup script in your fork of the repo
    tools/ "/Applications/Brackets Sprint"
    tools\setup_for_hacking.bat "C:\Program Files (x86)\Brackets Sprint 14"

Development Workflow

  • Launch Brackets
  • Debug > New Brackets Window
  • Edit / save in first window, reload / test in second window


  • Debug > Show Developer Tools brings up Web Inspector
  • Delete your prefs/cache
    ~/Library/Application Support/Brackets
  • Revert to installed Brackets source
    tools/ "/Applications/Brackets Sprint"
    tools\restore_installed_build.bat "C:\Program Files (x86)\Brackets Sprint 14"

Practice Good Hygiene

Unit Testing, Jasmine-style

describe("PreferenceStorage", function () {

    it("should read initial preferences from JSON", function () {

        var store = new PreferenceStorage(CLIENT_ID, 
            {"foo": "bar", hello: "world"});



Testing Asynchronous Sequences

it("should remove a list item when a file is closed", function () {
    // close the document
    var didClose = false, gotError = false;
    runs(function () {
            .done(function () { didClose = true; })
            .fail(function () { gotError = true; });
    waitsFor(function () { return didClose && !gotError; }, 
        "FILE_OPEN on file timeout", 1000);
    // check there are no list items
    runs(function () {
        var listItems = 
            testWindow.$("#open-files-container > ul").children();

Unit testing in Brackets

  • Core tests are in brackets/test/spec
  • Add new test files to brackets/test/UnitTestSuite.js
  • Test contexts:
    • True unit tests: run in test runner window
      Use require() for modules (private to test window)
    • Integration tests: (slower)

Code Complete! What Now?

  • Fill out the Contributor License Agreement (once)
  • Submit a pull request from your fork
  • Small changes are reviewed within a couple of days
  • Larger requests will be reviewed in a future sprint
  • Core team sprints are 2.5 weeks long

Writing Extensions

What You Can Build With Extensions

Try to build new features as extensions first

Minimize core code changes (and discuss with community)

How to Build an Extension

  • Create a folder or repo in extensions/user
  • Create a main.js file containing your main module
  • Use brackets.getModule() for core modules
  • Use require() for your own modules and strings
  • Use ExtensionUtils.loadStyleSheet() for styles
  • Create unittests.js for unit tests

Example: HelloWorld

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 doHelloWorld() {
        window.alert("Hello, world!");
    // Register command--can be called from menus, kbd shortcuts, etc.
    var MY_COMMAND_ID = "helloworld.sayhello";
    CommandManager.register("Hello World", MY_COMMAND_ID, doHelloWorld);

    // Create a menu item and key binding bound to the command
    var menu = Menus.getMenu(Menus.AppMenuBar.FILE_MENU);
    menu.addMenuItem(MY_COMMAND_ID, "Ctrl-Alt-H");

Code Hinting Extensions

Register with CodeHintManager.registerHintProvider()

    shouldShowHintsOnKey: function (string) {
        // quick shortcut check
        // return true if you want to pop up hints on this key

    getQueryInfo: function (editor, cursor) {
        // return the query object to be used for hinting
        // must have queryStr property--set to null if no hints
        // query object can have other properties
    search: function (query) {
        // takes the query object and performs the actual search
        // returns an array of matching strings
    handleSelect: function (string, editor, cursor) {
        // performs the edit based on the selected string

Quick Open Extensions

Register with QuickOpen.addQuickOpenPlugin()

    name: /* plugin name */,
    fileTypes: /* array of applicable types, or [] for all */,
    match: function (queryStr) {
        // returns true if this provider wants to handle the query
    search: function (queryStr) {
        // returns list of applicable items
        // items can be strings or objects with a "label" property
    itemFocus: function (item) {
        // called when user highlights an item
    itemSelect: function (item) { 
        // called when user chooses an item
    resultFormatter: function (queryStr, item) {
        // returns an <li> to display the given query and item object
    done: function () { 
        // called when quick open is closed

Quick Edit Extensions

Register with EditorManager.registerInlineEditProvider()

function myProvider(editor, pos) {
    // returns either a Promise that will be resolved with an inline widget
    // or null if we don't want to handle it

// Standard prototype chain hookup--do it however you like
function MyInlineWidget() {;
    // construct your widget's contents inside the $htmlContent node
MyInlineWidget.prototype = new InlineWidget();
MyInlineWidget.prototype.constructor = MyInlineWidget;
MyInlineWidget.prototype.parentClass = InlineWidget.prototype;

// can also implement onAdded, onClosed

Code Complete! What Now?

More Resources

If something's missing, please help us improve the docs!

#brackets on freenode