From 7d74ae971771092b4dd62490f4c652ae16c534ca Mon Sep 17 00:00:00 2001 From: Peter Flynn Date: Tue, 1 Apr 2014 16:30:43 -0700 Subject: [PATCH] - Fix bug #7300 (Prefs are never saved again after opening folder that contains the user prefs JSON files in its subtree) - ensure that FileSystem always knows we've read the JSON file before we try to write to it. We don't want to enable the 'blind' flag since it will mean we virtually always overwrite external changes to the prefs file, and we don't want to start using file watchers to observe external changes on the fly since we haven't yet deeply tested having multiple watch roots active at once. - Rename confusing "filename" vars in PreferencesBase - Fix JSLint errors in ProjectManager from #7026 --- src/preferences/PreferenceStorage.js | 2 ++ src/preferences/PreferencesBase.js | 22 +++++++++++----------- src/preferences/PreferencesImpl.js | 14 ++++++++++++++ src/project/ProjectManager.js | 18 +++++++++++++----- 4 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/preferences/PreferenceStorage.js b/src/preferences/PreferenceStorage.js index 202ca36f407..eb1e50f4f83 100644 --- a/src/preferences/PreferenceStorage.js +++ b/src/preferences/PreferenceStorage.js @@ -27,6 +27,8 @@ /** * PreferenceStorage defines an interface for persisting preference data as * name/value pairs for a module or plugin. + * + * @deprecated Use PreferencesManager APIs instead. */ define(function (require, exports, module) { "use strict"; diff --git a/src/preferences/PreferencesBase.js b/src/preferences/PreferencesBase.js index 14e9ce3178c..2a4ba5b118d 100644 --- a/src/preferences/PreferencesBase.js +++ b/src/preferences/PreferencesBase.js @@ -115,9 +115,9 @@ define(function (require, exports, module) { /** * MemoryStorage is not stored in a file, so fileChanged is ignored. * - * @param {string} filename File that has changed + * @param {string} filePath File that has changed */ - fileChanged: function (filename) { + fileChanged: function (filePath) { } }; @@ -241,10 +241,10 @@ define(function (require, exports, module) { /** * If the filename matches this Storage's path, a changed message is triggered. * - * @param {string} filename File that has changed + * @param {string} filePath File that has changed */ - fileChanged: function (filename) { - if (filename === this.path) { + fileChanged: function (filePath) { + if (filePath === this.path) { $(this).trigger("changed"); } } @@ -485,10 +485,10 @@ define(function (require, exports, module) { * Tells the Scope that the given file has been changed so that the * Storage can be reloaded if needed. * - * @param {string} filename Name of the file that has changed + * @param {string} filePath File that has changed */ - fileChanged: function (filename) { - this.storage.fileChanged(filename); + fileChanged: function (filePath) { + this.storage.fileChanged(filePath); }, /** @@ -1693,11 +1693,11 @@ define(function (require, exports, module) { * Tells the PreferencesSystem that the given file has been changed so that any * related Scopes can be reloaded. * - * @param {string} filename Name of the file that has changed + * @param {string} filePath File that has changed */ - fileChanged: function (filename) { + fileChanged: function (filePath) { _.forEach(this._scopes, function (scope) { - scope.fileChanged(filename); + scope.fileChanged(filePath); }); }, diff --git a/src/preferences/PreferencesImpl.js b/src/preferences/PreferencesImpl.js index 42fbc95c2ef..0ade1df22a9 100644 --- a/src/preferences/PreferencesImpl.js +++ b/src/preferences/PreferencesImpl.js @@ -32,6 +32,7 @@ define(function (require, exports, module) { "use strict"; var PreferencesBase = require("./PreferencesBase"), + ProjectManager = require("project/ProjectManager"), Async = require("utils/Async"), // The SETTINGS_FILENAME is used with a preceding "." within user projects @@ -103,6 +104,7 @@ define(function (require, exports, module) { }); }); + // "State" is stored like preferences but it is not generally intended to be user-editable. // It's for more internal, implicit things like window size, working set, etc. var stateManager = new PreferencesBase.PreferencesSystem(); @@ -112,6 +114,18 @@ define(function (require, exports, module) { smUserScope.addLayer(stateProjectLayer); var smUserScopeLoading = stateManager.addScope("user", smUserScope); + + // Listen for times where we might be unwatching a root that contains one of the user-level prefs files, + // and force a re-read of the file in order to ensure we can write to it later (see #7300). + $(ProjectManager).on("projectClose", function (event, rootDir) { + var prefsDir = brackets.app.getApplicationSupportDirectory() + "/"; + if (prefsDir.indexOf(rootDir.fullPath) === 0) { + manager.fileChanged(userPrefFile); + stateManager.fileChanged(userStateFile); + } + }); + + // Semi-Public API. Use this at your own risk. The public API is in PreferencesManager. exports.manager = manager; exports.projectStorage = projectStorage; diff --git a/src/project/ProjectManager.js b/src/project/ProjectManager.js index 929f7588b2c..f1d623a14bd 100644 --- a/src/project/ProjectManager.js +++ b/src/project/ProjectManager.js @@ -30,7 +30,8 @@ * the file tree. * * This module dispatches these events: - * - beforeProjectClose -- before _projectRoot changes + * - beforeProjectClose -- before _projectRoot changes, but working set files still open + * - projectClose -- *just* before _projectRoot changes; working set already cleared & project root unwatched * - beforeAppClose -- before Brackets quits entirely * - projectOpen -- after _projectRoot changes and the tree is re-rendered * - projectRefresh -- when project tree is re-rendered for a reason other than @@ -159,6 +160,7 @@ define(function (require, exports, module) { /** * @private * @see getProjectRoot() + * @type {Directory} */ var _projectRoot = null; @@ -674,15 +676,15 @@ define(function (require, exports, module) { var events = $._data(_projectTree[0], "events"), eventsForType = events ? events[type] : null, event = eventsForType ? _.find(eventsForType, function (e) { - return e.namespace === namespace && e.selector === selector; - }) : null, + return e.namespace === namespace && e.selector === selector; + }) : null, eventHandler = event ? event.handler : null; if (!eventHandler) { console.error(type + "." + namespace + " " + selector + " handler not found!"); } return eventHandler; }; - var createCustomHandler = function(originalHandler) { + var createCustomHandler = function (originalHandler) { return function (event) { var $node = $(event.target).parent("li"), methodName; @@ -1102,8 +1104,9 @@ define(function (require, exports, module) { if (_projectRoot && _projectRoot.fullPath === rootPath) { return (new $.Deferred()).resolve().promise(); } + + // About to close current project (if any) if (_projectRoot) { - // close current project $(exports).triggerHandler("beforeProjectClose", _projectRoot); } @@ -1111,6 +1114,11 @@ define(function (require, exports, module) { DocumentManager.closeAll(); _unwatchProjectRoot().always(function () { + // Done closing old project (if any) + if (_projectRoot) { + $(exports).triggerHandler("projectClose", _projectRoot); + } + startLoad.resolve(); }); }