-
Notifications
You must be signed in to change notification settings - Fork 183
Preferences System
This is a reference for writing code that uses the preferences APIs (for extension authors, etc.).
For how to use preferences as a Brackets end user, see How to Use Brackets - Preferences.
See also: Quick example with sample code
The preferences system has a collection of "scopes" that are searched in order for a given preferences key. There are a handful of ways to customize the behavior as needed, but a typical preference lookup will search scopes in this order:
- session: in-memory overrides that go away when Brackets is restarted. There wouldn't be anything in here by default.
- project: the .brackets.json file in the top-level directory of the project
- user: the brackets.json file in the user's appdata directory
- default: the default value as set by the
definePreference
call that declares the existence of a preference
If you set a value for a preference, the default behavior is to set that preference in the location from whence the preference's current value came. If the current value came from the default scope, then a call to set
will set the value in the user scope.
Preference lookup is further customized by "layers". There are two types of layers: PathLayer
and LanguageLayer
. PathLayer
is applied to the project scope that allows customization for specific files or sets of files in the project. LanguageLayer
is applied to both project scope and user scope: it allows specifying preferences based on the language of the file currently being edited, e.g. "css" or "javascript". The important thing to know about layers is that a preference lookup is done every time the user changes files in the editor. PathLayer
takes precedence over LanguageLayer
, meaning that if a preference value has been found in PathLayer
, then LanguageLayer
will not be consulted.
The stateManager
only has user and default scopes. It does have a ProjectLayer
attached to its user scope which puts values into project
object that is keyed on a project's top-level directory.
The PreferencesManager module defines two PreferencesSystem objects: one for prefs and one for "view state". What should be a pref and what should be view state? A pref is something that:
- The user may want to customize for a project and put in their version control system
- The user may want to edit themselves
Any other configuration value you need to save would be view state. For example, "spaceUnits" is a preference because it is something that a person would want to store in version control. "staticserver.port" is something a person would want to hand edit. Things like window sizes or the checked state of a given menu item would likely be view state.
The PreferencesManager module has convenience functions for working with the main preferencesManager and stateManager. Look in PreferencesBase to see everything that's available on a PreferencesSystem object.
If you are working with preferences and view state that are specific to your extension, you should prefix all of your preferences. There's an easy way to do that:
var PreferencesManager = brackets.getModule("preferences/PreferencesManager"),
prefs = PreferencesManager.getExtensionPrefs("myext"),
stateManager = PreferencesManager.stateManager.getPrefixedSystem("myext");
Now, anything pref you look up or set on prefs
or stateManager
will automatically have "myext." at the beginning.
If you're looking up global Brackets prefs (things like "spaceUnits"), you would just use the unprefixed functions.
Once you have your prefixed PreferencesSystem, you can then define a preference.
prefs.definePreference("greeting", "string", "Hello");
prefs.definePreference("showGreeting", "boolean", true).on("change", function () {
console.log("The value of showGreeting is now", prefs.get("showGreeting"));
});
Define a preference with the ID of the pref (these would be prefixed to be myext.greeting
and myext.showGreeting
), the type of the pref and the default value. You can also pass in an options object with name
and description
properties as a fourth parameter that could be used to generate a mythical, friendly-looking UI.
definePreference
returns a Preference object which allows you to listen for change events.
All the preferences defined are available as code hints in brackets.json
and .brackets.json
unless they are excluded. You can additionally configure code hint descriptions to each of the preference entry. See. this page for more details.
As noted in the last section, you can get change events for individual preference objects. You can also get change events for a whole PreferenceSystem:
prefs.on("change", function (e, data) {
console.log("Possibly changed prefs:", data.ids);
});
Important: Given the scoping for lookups and the way layers and scopes interact, its quite likely that you will get some change notifications when there wasn't actually a change. The notification is just telling you that a pref may have changed. It's up to you to then look up the current value of the pref for your context and decide if action needs to be taken.
The simplest form returns the current value at the highest precedence:
alert(prefs.get("greeting"));
You can optionally pass a context object in as a second parameter to be more specific about what you care about. If, for example, you have a preference that only makes sense at the project level (like Live Preview preferences), you can do this:
prefs.get("staticserver.port", PreferencesManager.CURRENT_PROJECT);
and the value will be looked up not for the current file (which could have come from outside of the project), but from the current project.
If the value of a preference is an object or an array, you will get back a clone of the original object, so you are free to modify it if you need to. A common case of this is modifying the value based on a user action and then saving the new value (see "Setting Prefs" below).
The simplest form of setting a pref:
prefs.set("greeting", "Namaste");
As described in the first section, this will set the pref in the same place as its current value is pulled from. You can also be specific about where you want the new value to go:
prefs.set("greeting", "Namaste", {
location: {
scope: "user"
}
});
This will force the greeting preference to be set to "Namaste" at the user level.
If you set the value to undefined
, the setting will be deleted at the appropriate level.
By default, preference changes set using the PreferencesManager module are automatically saved. So, in the example in the section above, the value of "Namaste" will be immediately saved to greeting
by the set()
call. If you're making a number of changes in a row, you may want to pass in the doNotSave
flag (set the 4th argument to set()
to true
) and then explicitly call save
. This can be faster because it won't write to the file repeatedly, but it's an optimization you usually won't need to worry about.
Currently, there is no standard user interface for preferences. There is a backlog item for one.
Prior to Brackets 36, PreferencesManager provided preferences that were namespaced to a given module and stored in localStorage. In Brackets 36, there is a new preferences system and, starting with Brackets 37, the old preferences system has been deprecated and its use should be discontinued.
The PreferencesManager module has a utility function, convertPreferences
that will help convert from the old system to the new. The common case is like the one in the CodeInspection module:
PreferencesManager.convertPreferences(module, {
"enabled": "user linting.enabled",
"collapsed": "user linting.collapsed"
});
This will convert the old "enabled" preference for the CodeInspection module to a new user-level preference called "linting.enabled". The conversion will only be done once, and the old value is saved in localStorage so that a user can switch between older and newer versions of Brackets.
Some parts of Brackets stored information for specific files or projects in the old preferences system using keys that have a prefix. For example, DocumentManager stored working set files this way. The new preferences system stores the data in JSON objects, making it easier to have nested structures, but the conversion is more complex. Take a look at DocumentManager to see this more complex form of convertPreferences
in action.
Default Preferences (JSON)
{
"useTabChar": false,
"tabSize": 8,
"spaceUnits": 4,
"closeBrackets": false,
"showLineNumbers": true,
"styleActiveLine": false,
"wordWrap": false,
"linting.enabled": true,
"linting.collapsed": false,
"quickview.enabled": true
}