Modules (181)

PreferencesBase

Description

Infrastructure for the preferences system.

At the top, the level at which most people will interact, is the PreferencesSystem object. The most common operation is get(id), which simply retrieves the value of a given preference.

The PreferencesSystem has a collection of Scopes, which it traverses in a specified order. Each Scope holds one level of settings.

PreferencesManager.js sets up a singleton PreferencesSystem that has the following Scopes:

  • default (the default values for any settings that are explicitly registered)
  • user (the user's customized settings – the equivalent of Brackets' old localStorage-based system. This is the settings file that lives in AppData)
  • Additional scopes for each .brackets.json file going upward in the file tree from the current file

For example, if spaceUnits has a value set in a .brackets.json file near the open file, then a call to get("spaceUnits") would return the value from that file. File values come first, user values next, default values last. If the setting is not known at all, undefined is returned.

Each Scope has an associated Storage object that knows how to load and save the preferences value for that Scope. There are two implementations: MemoryStorage and FileStorage.

The final concept used is that of Layers, which can be added to Scopes. Generally, a Layer looks for a collection of preferences that are nested in some fashion in the Scope's data. Under certain circumstances (decided upon by the Layer object), those nested preferences will take precedence over the main preferences in the Scope.

Dependencies

Functions

Private

_addEventDispatcherImpl

Utility for PreferencesSystem & PrefixedPreferencesSystem -- attach EventDispatcher's on()/off() implementation as private _on_internal()/_off_internal() methods, so the custom on()/off() APIs these classes use can leverage EventDispatcher code internally. Also attach the regular public trigger().

    function _addEventDispatcherImpl(proto) {
        var temp = {};
        EventDispatcher.makeEventDispatcher(temp);
        proto._on_internal  = temp.on;
        proto._off_internal = temp.off;
        proto.trigger       = temp.trigger;
    }
Private

_findMatchingGlob

pathData
Object
The keys are globs and the values are the preferences for that glob
filename
string
relative filename to match against the globs
Returns:
?string
glob pattern that matched, if any
    function _findMatchingGlob(pathData, filename) {
        var globs = Object.keys(pathData),
            globCounter;

        if (!filename) {
            return;
        }

        for (globCounter = 0; globCounter < globs.length; globCounter++) {
            var glob = globs[globCounter];

            if (globmatch(filename, glob)) {
                return glob;
            }
        }
    }

Classes

Constructor

MemoryStorage

MemoryStorage, as the name implies, stores the preferences in memory. This is suitable for single session data or testing.

data
Object
Initial data for the storage.
    function MemoryStorage(data) {
        this.data = data || {};
    }

    MemoryStorage.prototype = {
Constructor

ParsingError

Error type for problems parsing preference files.

message
string
Error message
    function ParsingError(message) {
        this.name = "ParsingError";
        this.message = message || "";
    }

    ParsingError.prototype = new Error();
Constructor

FileStorage

Loads/saves preferences from a JSON file on disk.

path
string
Path to the preferences file
createIfMissing
boolean
True if the file should be created if it doesn't exist. If this is not true, an exception will be thrown if the file does not exist.
recreateIfInvalid
boolean
True if the file needs to be recreated if it is invalid. Invalid- Either unreadable or unparseable. The invalid copy will be sent to trash in case the user wants to refer to it.
    function FileStorage(path, createIfMissing, recreateIfInvalid) {
        this.path = path;
        this.createIfMissing = createIfMissing;
        this.recreateIfInvalid = recreateIfInvalid;
        this._lineEndings = FileUtils.getPlatformLineEndings();
    }

    FileStorage.prototype = {
Constructor

Scope

A Scope is a data container that is tied to a Storage.

Additionally, Scopes support "layers" which are additional levels of preferences that are stored within a single preferences file.

storage
Storage
Storage object from which prefs are loaded/saved
    function Scope(storage) {
        this.storage = storage;
        storage.on("changed", this.load.bind(this));
        this.data = {};
        this._dirty = false;
        this._layers = [];
        this._layerMap = {};
        this._exclusions = [];
    }

    _.extend(Scope.prototype, {
Constructor

ProjectLayer

Create a default project layer object that has a single property "key" with "project" as its value.

    function ProjectLayer() {
        this.projectPath = null;
    }

    ProjectLayer.prototype = {
        key: "project",
Constructor

LanguageLayer

    function LanguageLayer() {
    }

    LanguageLayer.prototype = {
        key: "language",
Constructor

PathLayer

There can be multiple paths and they are each checked in turn. The first that matches the currently edited file wins.

prefFilePath
string
path to the preference file
    function PathLayer(prefFilePath) {
        this.setPrefFilePath(prefFilePath);
    }

    PathLayer.prototype = {
        key: "path",
Constructor

Preference

Represents a single, known Preference.

properties
Object
Information about the Preference that is stored on this object
    function Preference(properties) {
        _.extend(this, properties);
    }

    EventDispatcher.makeEventDispatcher(Preference.prototype);
Constructor

PrefixedPreferencesSystem

Provides a subset of the PreferencesSystem functionality with preference access always occurring with the given prefix.

base
PreferencesSystem
The real PreferencesSystem that is backing this one
prefix
string
Prefix that is used for preferences lookup. Any separator characters should already be added.
    function PrefixedPreferencesSystem(base, prefix) {
        this.base = base;
        this.prefix = prefix;
        this._listenerInstalled = false;
    }

    PrefixedPreferencesSystem.prototype = {
Constructor

PreferencesSystem

PreferencesSystem ties everything together to provide a simple interface for managing the whole prefs system.

It keeps track of multiple Scope levels and also manages path-based Scopes.

It also provides the ability to register preferences, which gives a fine-grained means for listening for changes and will ultimately allow for automatic UI generation.

The contextBuilder is used to construct get/set contexts based on the needs of individual context systems. It can be passed in at construction time or set later.

contextNormalizer
function
function that is passed the context used for get or set to adjust for specific PreferencesSystem behavior
    function PreferencesSystem(contextBuilder) {
        this.contextBuilder = contextBuilder;

        this._knownPrefs = {};
        this._scopes = {
            "default": new Scope(new MemoryStorage())
        };

        this._scopes["default"].load();

        this._defaults = {
            scopeOrder: ["default"],
            _shadowScopeOrder: [{
                id: "default",
                scope: this._scopes["default"],
                promise: (new $.Deferred()).resolve().promise()
            }]
        };

        this._pendingScopes = {};

        this._saveInProgress = false;
        this._nextSaveDeferred = null;

        // The objects that define the different kinds of path-based Scope handlers.
        // Examples could include the handler for .brackets.json files or an .editorconfig
        // handler.
        this._pathScopeDefinitions = {};

        // Names of the files that contain path scopes
        this._pathScopeFilenames = [];

        // Keeps track of cached path scope objects.
        this._pathScopes = {};

        // Keeps track of change events that need to be sent when change events are resumed
        this._changeEventQueue = null;

        var notifyPrefChange = function (id) {
            var pref = this._knownPrefs[id];
            if (pref) {
                pref.trigger(PREFERENCE_CHANGE);
            }
        }.bind(this);

        // When we signal a general change message on this manager, we also signal a change
        // on the individual preference object.
        this.on(PREFERENCE_CHANGE, function (e, data) {
            data.ids.forEach(notifyPrefChange);
        }.bind(this));
    }

    _.extend(PreferencesSystem.prototype, {