Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.

Commit 6e00b13

Browse files
committed
Fix #7442: make API consistent between PrefMan and Prefixed PrefMan.
The slightly higher level API provided by PreferencesManager (over the PreferencesBase.PreferencesSystem) worked nicely but was inconsistent with the API an extension author gets when they call `PreferencesManager.getExtensionPrefs`. This change pushes that extra behavior down into PreferencesBase.PreferencesSystem so that the APIs are consistent. This is a non-breaking change that basically adds to the API: * doNotSave flag for set: calls to `set()` save automatically unless this flag is set. If extension code is already calling `save()`, that means that there will be an extra call to `save()`, but that should have minimal performance impact because `save()` does no saving when the scopes aren't dirty * support for string contexts: extensions would have to use complete object contexts previously. This adds support for the simpler string contexts and doesn't change the behavior for object contexts.
1 parent ff87510 commit 6e00b13

3 files changed

Lines changed: 81 additions & 58 deletions

File tree

src/preferences/PreferencesBase.js

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -927,10 +927,12 @@ define(function (require, exports, module) {
927927
* @param {string} id Identifier of the preference to set
928928
* @param {Object} value New value for the preference
929929
* @param {{location: ?Object, context: ?Object}=} options Specific location in which to set the value or the context to use when setting the value
930-
* @return {boolean} true if a value was set
930+
* @param {boolean=} doNotSave True if the preference change should not be saved automatically.
931+
* @return {valid: {boolean}, true if no validator specified or if value is valid
932+
* stored: {boolean}} true if a value was stored
931933
*/
932-
set: function (id, value, options) {
933-
return this.base.set(this.prefix + id, value, options);
934+
set: function (id, value, options, doNotSave) {
935+
return this.base.set(this.prefix + id, value, options, doNotSave);
934936
},
935937

936938
/**
@@ -1035,8 +1037,15 @@ define(function (require, exports, module) {
10351037
*
10361038
* It also provides the ability to register preferences, which gives a fine-grained
10371039
* means for listening for changes and will ultimately allow for automatic UI generation.
1040+
*
1041+
* The contextNormalizer is used to customize get/set contexts based on the needs of individual
1042+
* context systems. It can be passed in at construction time or set later.
1043+
*
1044+
* @param {function=} contextNormalizer function that is passed the context used for get or set to adjust for specific PreferencesSystem behavior
10381045
*/
1039-
function PreferencesSystem() {
1046+
function PreferencesSystem(contextNormalizer) {
1047+
this.contextNormalizer = contextNormalizer;
1048+
10401049
this._knownPrefs = {};
10411050
this._scopes = {
10421051
"default": new Scope(new MemoryStorage())
@@ -1339,8 +1348,13 @@ define(function (require, exports, module) {
13391348
* @return {{scopeOrder: string, filename: ?string}} context object
13401349
*/
13411350
_getContext: function (context) {
1342-
context = context || this._defaultContext;
1343-
return context;
1351+
if (context) {
1352+
if (this.contextNormalizer) {
1353+
context = this.contextNormalizer(context);
1354+
}
1355+
return context;
1356+
}
1357+
return this._defaultContext;
13441358
},
13451359

13461360
/**
@@ -1485,10 +1499,11 @@ define(function (require, exports, module) {
14851499
* @param {string} id Identifier of the preference to set
14861500
* @param {Object} value New value for the preference
14871501
* @param {{location: ?Object, context: ?Object}=} options Specific location in which to set the value or the context to use when setting the value
1502+
* @param {boolean=} doNotSave True if the preference change should not be saved automatically.
14881503
* @return {valid: {boolean}, true if no validator specified or if value is valid
14891504
* stored: {boolean}} true if a value was stored
14901505
*/
1491-
set: function (id, value, options) {
1506+
set: function (id, value, options, doNotSave) {
14921507
options = options || {};
14931508
var context = this._getContext(options.context),
14941509

@@ -1525,6 +1540,9 @@ define(function (require, exports, module) {
15251540

15261541
var wasSet = scope.set(id, value, context, location);
15271542
if (wasSet) {
1543+
if (!doNotSave) {
1544+
this.save();
1545+
}
15281546
this._triggerChange({
15291547
ids: [id]
15301548
});

src/preferences/PreferencesManager.js

Lines changed: 5 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,8 @@ define(function (require, exports, module) {
446446
return context;
447447
}
448448

449+
PreferencesImpl.manager.contextNormalizer = _normalizeContext;
450+
449451
/**
450452
* @private
451453
*
@@ -463,50 +465,6 @@ define(function (require, exports, module) {
463465

464466
PreferencesImpl.manager.on("scopeOrderChange", _updateCurrentProjectContext);
465467

466-
/**
467-
* Look up a preference in the given context. The default is
468-
* CURRENT_FILE (preferences as they would be applied to the
469-
* currently edited file).
470-
*
471-
* @param {string} id Preference ID to retrieve the value of
472-
* @param {Object|string=} context CURRENT_FILE, CURRENT_PROJECT or a filename
473-
*/
474-
function get(id, context) {
475-
context = _normalizeContext(context);
476-
return PreferencesImpl.manager.get(id, context);
477-
}
478-
479-
/**
480-
* Sets a preference and notifies listeners that there may
481-
* have been a change. By default, the preference is set in the same location in which
482-
* it was defined except for the "default" scope. If the current value of the preference
483-
* comes from the "default" scope, the new value will be set at the level just above
484-
* default.
485-
*
486-
* The preferences are saved automatically unless doNotSave is true.
487-
*
488-
* As with the `get()` function, the context can be a filename,
489-
* CURRENT_FILE, CURRENT_PROJECT or a full context object as supported by
490-
* PreferencesSystem.
491-
*
492-
* @param {string} id Identifier of the preference to set
493-
* @param {Object} value New value for the preference
494-
* @param {{location: ?Object, context: ?Object|string}=} options Specific location in which to set the value or the context to use when setting the value
495-
* @param {boolean=} doNotSave True if the preference change should not be saved automatically.
496-
* @return {valid: {boolean}, true if no validator specified or if value is valid
497-
* stored: {boolean}} true if a value was stored
498-
*/
499-
function set(id, value, options, doNotSave) {
500-
if (options && options.context) {
501-
options.context = _normalizeContext(options.context);
502-
}
503-
var wasSet = PreferencesImpl.manager.set(id, value, options);
504-
if (!doNotSave) {
505-
PreferencesImpl.manager.save();
506-
}
507-
return wasSet;
508-
}
509-
510468
/**
511469
* @private
512470
*/
@@ -541,7 +499,7 @@ define(function (require, exports, module) {
541499
*/
542500
function setValueAndSave(id, value, options) {
543501
DeprecationWarning.deprecationWarning("setValueAndSave called for " + id + ". Use set instead.");
544-
var changed = set(id, value, options).stored;
502+
var changed = exports.set(id, value, options).stored;
545503
PreferencesImpl.manager.save();
546504
return changed;
547505
}
@@ -594,8 +552,8 @@ define(function (require, exports, module) {
594552

595553
exports.ready = PreferencesImpl.managerReady;
596554
exports.getUserPrefFile = getUserPrefFile;
597-
exports.get = get;
598-
exports.set = set;
555+
exports.get = PreferencesImpl.manager.get.bind(PreferencesImpl.manager);
556+
exports.set = PreferencesImpl.manager.set.bind(PreferencesImpl.manager);
599557
exports.save = PreferencesImpl.manager.save.bind(PreferencesImpl.manager);
600558
exports.on = PreferencesImpl.manager.on.bind(PreferencesImpl.manager);
601559
exports.off = PreferencesImpl.manager.off.bind(PreferencesImpl.manager);

test/spec/PreferencesBase-test.js

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -281,16 +281,18 @@ define(function (require, exports, module) {
281281
expect(pm.set("foo", foo).stored).toBe(true);
282282

283283
expect(pm.get("foo").value).toBe(42);
284-
expect(scope._dirty).toBe(true);
284+
expect(scope._dirty).toBe(false);
285285

286286
// Explicitly save it in order to clear dirty flag.
287287
pm.save();
288288
expect(scope._dirty).toBe(false);
289289

290290
foo.value = "!!!";
291291
expect(foo.value).toBe("!!!");
292-
expect(pm.set("foo", foo).stored).toBe(true);
292+
expect(pm.set("foo", foo, undefined, true).stored).toBe(true);
293293
expect(scope._dirty).toBe(true);
294+
pm.save();
295+
expect(scope._dirty).toBe(false);
294296

295297
var fooCopyFromPref = pm.get("foo");
296298
expect(fooCopyFromPref.value).toBe("!!!");
@@ -1013,7 +1015,8 @@ define(function (require, exports, module) {
10131015

10141016
it("can provide an automatically prefixed version of itself", function () {
10151017
var pm = new PreferencesBase.PreferencesSystem();
1016-
pm.addScope("user", new PreferencesBase.MemoryStorage());
1018+
var scope = new PreferencesBase.Scope(new PreferencesBase.MemoryStorage());
1019+
pm.addScope("user", scope);
10171020
pm.set("spaceUnits", 10);
10181021
pm.set("linting.enabled", true);
10191022

@@ -1032,13 +1035,17 @@ define(function (require, exports, module) {
10321035
scope: "user"
10331036
});
10341037

1035-
prefixedPM.set("collapsed", false);
1038+
// set the value with doNotSave set. Will check to verify that save did not occur.
1039+
prefixedPM.set("collapsed", false, undefined, true);
10361040

10371041
expect(events).toEqual([{
10381042
ids: ["collapsed"]
10391043
}]);
10401044

10411045
expect(prefixedPM.get("collapsed")).toBe(false);
1046+
1047+
// verify that the scope was not saved automatically
1048+
expect(scope._dirty).toBe(true);
10421049
expect(pm.get("collapsed")).toBeUndefined();
10431050
expect(pm.get("linting.collapsed")).toBe(false);
10441051

@@ -1080,6 +1087,46 @@ define(function (require, exports, module) {
10801087
expect(pm.set("spaceUnits", -1).valid).toBe(false); // fail: out-of-range lower
10811088
expect(pm.get("spaceUnits")).toBe(4); // expect default
10821089
});
1090+
1091+
it("should handle context normalization", function () {
1092+
var normalizer = function (context) {
1093+
if (typeof context === "string") {
1094+
return {
1095+
scopeOrder: [context]
1096+
};
1097+
}
1098+
return context;
1099+
};
1100+
1101+
var pm = new PreferencesBase.PreferencesSystem(normalizer);
1102+
pm.addScope("user", new PreferencesBase.MemoryStorage({
1103+
value: 1
1104+
}));
1105+
pm.addScope("session", new PreferencesBase.MemoryStorage({
1106+
value: 2
1107+
}));
1108+
expect(pm.get("value")).toBe(2);
1109+
1110+
// Test passing in a string for the get context
1111+
expect(pm.get("value", "user")).toBe(1);
1112+
1113+
// This will set in the scope in which the value was set. Without a context,
1114+
// that means "session".
1115+
pm.set("value", 3);
1116+
expect(pm.get("value")).toBe(3);
1117+
expect(pm.get("value", "user")).toBe(1);
1118+
1119+
// Now, set with a context. This should cause the value to be set in "user"
1120+
// scope.
1121+
pm.set("value", 4, {
1122+
context: "user"
1123+
});
1124+
expect(pm.get("value")).toBe(3);
1125+
expect(pm.get("value", "user")).toBe(4);
1126+
1127+
expect(pm.getPreferenceLocation("value").scope).toBe("session");
1128+
expect(pm.getPreferenceLocation("value", "user").scope).toBe("user");
1129+
});
10831130
});
10841131

10851132
describe("File Storage", function () {

0 commit comments

Comments
 (0)