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

Commit 90398b4

Browse files
committed
Merge pull request #8444 from adobe/pflynn/language-switcher-improvements
Language switcher: connect to prefs & clean up some code
2 parents 167cd18 + ba1427c commit 90398b4

File tree

13 files changed

+190
-137
lines changed

13 files changed

+190
-137
lines changed

src/document/Document.js

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ define(function (require, exports, module) {
6565
*
6666
* __deleted__ -- When the file for this document has been deleted. All views onto the document should
6767
* be closed. The document will no longer be editable or dispatch "change" events.
68+
*
69+
* __languageChanged__ -- When the value of getLanguage() has changed. 2nd argument is the old value,
70+
* 3rd argument is the new value.
6871
*
6972
* @constructor
7073
* @param {!File} file Need not lie within the project.
@@ -672,19 +675,7 @@ define(function (require, exports, module) {
672675
};
673676

674677
/**
675-
* Overrides the default language of this document and sets it to the given
676-
* language. This change is not persisted if the document is closed.
677-
* @param {?Language} language The language to be set for this document; if
678-
* null, the language will be set back to the default.
679-
*/
680-
Document.prototype.setLanguageOverride = function (language) {
681-
LanguageManager._setLanguageOverrideForPath(this.file.fullPath, language);
682-
this._updateLanguage();
683-
};
684-
685-
/**
686-
* Updates the language according to the file extension. If the current
687-
* language was forced (set manually by user), don't change it.
678+
* Updates the language to match the current mapping given by LanguageManager
688679
*/
689680
Document.prototype._updateLanguage = function () {
690681
var oldLanguage = this.language;

src/editor/EditorStatusBar.js

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ define(function (require, exports, module) {
3838
DropdownButton = require("widgets/DropdownButton").DropdownButton,
3939
EditorManager = require("editor/EditorManager"),
4040
Editor = require("editor/Editor").Editor,
41+
FileUtils = require("file/FileUtils"),
4142
KeyEvent = require("utils/KeyEvent"),
4243
LanguageManager = require("language/LanguageManager"),
44+
PreferencesManager = require("preferences/PreferencesManager"),
4345
StatusBar = require("widgets/StatusBar"),
4446
Strings = require("strings"),
4547
StringUtils = require("utils/StringUtils");
@@ -53,6 +55,9 @@ define(function (require, exports, module) {
5355
$indentWidthInput,
5456
$statusOverwrite;
5557

58+
/** Special list item for the 'set as default' gesture in language switcher dropdown */
59+
var LANGUAGE_SET_AS_DEFAULT = {};
60+
5661

5762
/**
5863
* Determine string based on count
@@ -301,6 +306,9 @@ define(function (require, exports, module) {
301306

302307
languageSelect.items = languages;
303308

309+
// Add option to top of menu for persisting the override
310+
languageSelect.items.unshift("---");
311+
languageSelect.items.unshift(LANGUAGE_SET_AS_DEFAULT);
304312
}
305313

306314
/**
@@ -317,8 +325,14 @@ define(function (require, exports, module) {
317325

318326
languageSelect = new DropdownButton("", [], function (item, index) {
319327
var document = EditorManager.getActiveEditor().document,
320-
defaultLang = LanguageManager.getLanguageForPath(document.file.fullPath, true),
321-
html = _.escape(item.getName());
328+
defaultLang = LanguageManager.getLanguageForPath(document.file.fullPath, true);
329+
330+
if (item === LANGUAGE_SET_AS_DEFAULT) {
331+
var label = _.escape(StringUtils.format(Strings.STATUSBAR_SET_DEFAULT_LANG, FileUtils.getSmartFileExtension(document.file.fullPath)));
332+
return { html: label, enabled: document.getLanguage() !== defaultLang };
333+
}
334+
335+
var html = _.escape(item.getName());
322336

323337
// Show indicators for currently selected & default languages for the current file
324338
if (item === defaultLang) {
@@ -363,13 +377,22 @@ define(function (require, exports, module) {
363377
$indentWidthInput.focus(function () { $indentWidthInput.select(); });
364378

365379
// Language select change handler
366-
$(languageSelect).on("select", function (e, lang, index) {
380+
$(languageSelect).on("select", function (e, lang) {
367381
var document = EditorManager.getActiveEditor().document,
368-
fullPath = document.file.fullPath,
369-
defaultLang = LanguageManager.getLanguageForPath(fullPath, true);
370-
// if default language selected, don't "force" it
371-
// (passing in null will reset the force flag)
372-
document.setLanguageOverride(lang === defaultLang ? null : lang);
382+
fullPath = document.file.fullPath;
383+
384+
if (lang === LANGUAGE_SET_AS_DEFAULT) {
385+
// Set file's current language in preferences as a file extension override (only enabled if not default already)
386+
var fileExtensionMap = PreferencesManager.get("language.fileExtensions");
387+
fileExtensionMap[FileUtils.getSmartFileExtension(fullPath)] = document.getLanguage().getId();
388+
PreferencesManager.set("language.fileExtensions", fileExtensionMap);
389+
390+
} else {
391+
// Set selected language as a path override for just this one file (not persisted)
392+
var defaultLang = LanguageManager.getLanguageForPath(fullPath, true);
393+
// if default language selected, pass null to clear the override
394+
LanguageManager.setLanguageOverrideForPath(fullPath, lang === defaultLang ? null : lang);
395+
}
373396
});
374397

375398
$statusOverwrite.on("click", _updateEditorOverwriteMode);

src/extensibility/Package.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ define(function (require, exports, module) {
4141
NodeConnection = require("utils/NodeConnection"),
4242
PreferencesManager = require("preferences/PreferencesManager");
4343

44-
PreferencesManager.definePreference("proxy", "string");
44+
PreferencesManager.definePreference("proxy", "string", undefined);
4545

4646
var Errors = {
4747
ERROR_LOADING: "ERROR_LOADING",

src/extensions/default/JSLint/main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ define(function (require, exports, module) {
5050
*/
5151
var _lastRunOptions;
5252

53-
prefs.definePreference("options", "object")
53+
prefs.definePreference("options", "object", undefined)
5454
.on("change", function (e, data) {
5555
var options = prefs.get("options");
5656
if (!_.isEqual(options, _lastRunOptions)) {

src/extensions/default/JavaScriptCodeHints/ScopeManager.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,9 +1129,9 @@ define(function (require, exports, module) {
11291129
/**
11301130
* Do the work to initialize a code hinting session.
11311131
*
1132-
* @param {Session} session - the active hinting session
1133-
* @param {Document} document - the document the editor has changed to
1134-
* @param {Document} previousDocument - the document the editor has changed from
1132+
* @param {Session} session - the active hinting session (TODO: currently unused)
1133+
* @param {!Document} document - the document the editor has changed to
1134+
* @param {?Document} previousDocument - the document the editor has changed from
11351135
*/
11361136
function doEditorChange(session, document, previousDocument) {
11371137
var file = document.file,
@@ -1229,9 +1229,9 @@ define(function (require, exports, module) {
12291229
/**
12301230
* Called each time a new editor becomes active.
12311231
*
1232-
* @param {Session} session - the active hinting session
1233-
* @param {Document} document - the document of the editor that has changed
1234-
* @param {Document} previousDocument - the document of the editor is changing from
1232+
* @param {Session} session - the active hinting session (TODO: currently unused by doEditorChange())
1233+
* @param {!Document} document - the document of the editor that has changed
1234+
* @param {?Document} previousDocument - the document of the editor is changing from
12351235
*/
12361236
function handleEditorChange(session, document, previousDocument) {
12371237
if (addFilesPromise === null) {
@@ -1469,7 +1469,7 @@ define(function (require, exports, module) {
14691469
*
14701470
* @param {Session} session - the active hinting session
14711471
* @param {Document} document - the document of the editor that has changed
1472-
* @param {Document} previousDocument - the document of the editor is changing from
1472+
* @param {?Document} previousDocument - the document of the editor is changing from
14731473
*/
14741474
function handleEditorChange(session, document, previousDocument) {
14751475

src/extensions/default/JavaScriptCodeHints/main.js

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -585,8 +585,8 @@ define(function (require, exports, module) {
585585
* When the editor is changed, reset the hinting session and cached
586586
* information, and reject any pending deferred requests.
587587
*
588-
* @param {Editor} editor - editor context to be initialized.
589-
* @param {Editor} previousEditor - the previous editor.
588+
* @param {!Editor} editor - editor context to be initialized.
589+
* @param {?Editor} previousEditor - the previous editor.
590590
*/
591591
function initializeSession(editor, previousEditor) {
592592
session = new Session(editor);
@@ -597,11 +597,11 @@ define(function (require, exports, module) {
597597
}
598598

599599
/*
600-
* Install editor change listeners
600+
* Connects to the given editor, creating a new Session & adding listeners
601601
*
602-
* @param {Editor} editor - editor context on which to listen for
603-
* changes
604-
* @param {Editor} previousEditor - the previous editor
602+
* @param {?Editor} editor - editor context on which to listen for
603+
* changes. If null, 'session' is cleared.
604+
* @param {?Editor} previousEditor - the previous editor
605605
*/
606606
function installEditorListeners(editor, previousEditor) {
607607
// always clean up cached scope and hint info
@@ -650,18 +650,21 @@ define(function (require, exports, module) {
650650
* @param {Editor} previous - the previous editor context
651651
*/
652652
function handleActiveEditorChange(event, current, previous) {
653-
// Uninstall "languageChanged" event listeners on the previous editor's document
654-
if (previous && previous !== current) {
653+
// Uninstall "languageChanged" event listeners on previous editor's document & put them on current editor's doc
654+
if (previous) {
655655
$(previous.document)
656656
.off(HintUtils.eventName("languageChanged"));
657657
}
658-
if (current && current.document !== DocumentManager.getCurrentDocument()) {
658+
if (current) {
659659
$(current.document)
660660
.on(HintUtils.eventName("languageChanged"), function () {
661+
// If current doc's language changed, reset our state by treating it as if the user switched to a
662+
// different document altogether
661663
uninstallEditorListeners(current);
662664
installEditorListeners(current);
663665
});
664666
}
667+
665668
uninstallEditorListeners(previous);
666669
installEditorListeners(current, previous);
667670
}
@@ -829,13 +832,6 @@ define(function (require, exports, module) {
829832
.on(HintUtils.eventName("activeEditorChange"),
830833
handleActiveEditorChange);
831834

832-
$(DocumentManager)
833-
.on("currentDocumentLanguageChanged", function (e) {
834-
var activeEditor = EditorManager.getActiveEditor();
835-
uninstallEditorListeners(activeEditor);
836-
installEditorListeners(activeEditor);
837-
});
838-
839835
$(ProjectManager).on("beforeProjectClose", function () {
840836
ScopeManager.handleProjectClose();
841837
});

src/language/LanguageManager.js

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@
120120
* isBinary: true
121121
* });
122122
*
123+
*
124+
* LanguageManager dispatches two events:
125+
*
126+
* - languageAdded -- When any new Language is added. 2nd arg is the new Language.
127+
* - languageModified -- When the attributes of a Language change, or when the Language gains or loses
128+
* file extension / filename mappings. 2nd arg is the modified Language.
123129
*/
124130
define(function (require, exports, module) {
125131
"use strict";
@@ -227,28 +233,6 @@ define(function (require, exports, module) {
227233
_modeToLanguageMap[mode] = language;
228234
}
229235

230-
/**
231-
* Adds a language mapping for the specified fullPath. If language is falsy (null or undefined), the mapping
232-
* is removed.
233-
*
234-
* @param {!fullPath} fullPath absolute path of the file
235-
* @param {?object} language language to associate the file with or falsy value to remove the existing mapping
236-
*/
237-
function _setLanguageOverrideForPath(fullPath, language) {
238-
if (!language) {
239-
delete _filePathToLanguageMap[fullPath];
240-
} else {
241-
_filePathToLanguageMap[fullPath] = language;
242-
}
243-
}
244-
245-
/**
246-
* Resets all the language overrides for file paths. Used by unit tests only.
247-
*/
248-
function _resetLanguageOverrides() {
249-
_filePathToLanguageMap = {};
250-
}
251-
252236
/**
253237
* Resolves a language ID to a Language object.
254238
* File names have a higher priority than file extensions.
@@ -260,7 +244,9 @@ define(function (require, exports, module) {
260244
}
261245

262246
/**
263-
* Resolves a language to a file extension
247+
* Resolves a file extension to a Language object.
248+
* *Warning:* it is almost always better to use getLanguageForPath(), since Language can depend
249+
* on file name and even full path. Use this API only if no relevant file/path exists.
264250
* @param {!string} extension Extension that language should be resolved for
265251
* @return {?Language} The language for the provided extension or null if none exists
266252
*/
@@ -383,6 +369,37 @@ define(function (require, exports, module) {
383369
$(exports).triggerHandler("languageModified", [language]);
384370
}
385371

372+
/**
373+
* Adds a language mapping for the specified fullPath. If language is falsy (null or undefined), the mapping
374+
* is removed. The override is NOT persisted across Brackets sessions.
375+
*
376+
* @param {!fullPath} fullPath absolute path of the file
377+
* @param {?object} language language to associate the file with or falsy value to remove any existing override
378+
*/
379+
function setLanguageOverrideForPath(fullPath, language) {
380+
var oldLang = getLanguageForPath(fullPath);
381+
if (!language) {
382+
delete _filePathToLanguageMap[fullPath];
383+
} else {
384+
_filePathToLanguageMap[fullPath] = language;
385+
}
386+
var newLang = getLanguageForPath(fullPath);
387+
388+
// Old language changed since this path is no longer mapped to it
389+
_triggerLanguageModified(oldLang);
390+
// New language changed since a path is now mapped to it that wasn't before
391+
_triggerLanguageModified(newLang);
392+
}
393+
394+
/**
395+
* Resets all the language overrides for file paths. Used by unit tests only.
396+
*/
397+
function _resetPathLanguageOverrides() {
398+
_filePathToLanguageMap = {};
399+
}
400+
401+
402+
386403

387404
/**
388405
* Model for a language.
@@ -989,7 +1006,7 @@ define(function (require, exports, module) {
9891006
* }
9901007
*/
9911008
function _updateFromPrefs(pref) {
992-
var newMapping = PreferencesManager.get(pref) || {},
1009+
var newMapping = PreferencesManager.get(pref),
9931010
newNames = Object.keys(newMapping),
9941011
state = _prefState[pref],
9951012
last = state.last,
@@ -1086,25 +1103,21 @@ define(function (require, exports, module) {
10861103
// depends on FileUtils) here. Using the async form of require fixes this.
10871104
require(["preferences/PreferencesManager"], function (pm) {
10881105
PreferencesManager = pm;
1089-
_updateFromPrefs(_EXTENSION_MAP_PREF);
1090-
_updateFromPrefs(_NAME_MAP_PREF);
1091-
pm.definePreference(_EXTENSION_MAP_PREF, "object").on("change", function () {
1106+
pm.definePreference(_EXTENSION_MAP_PREF, "object", {}).on("change", function () {
10921107
_updateFromPrefs(_EXTENSION_MAP_PREF);
10931108
});
1094-
pm.definePreference(_NAME_MAP_PREF, "object").on("change", function () {
1109+
pm.definePreference(_NAME_MAP_PREF, "object", {}).on("change", function () {
10951110
_updateFromPrefs(_NAME_MAP_PREF);
10961111
});
1112+
_updateFromPrefs(_EXTENSION_MAP_PREF);
1113+
_updateFromPrefs(_NAME_MAP_PREF);
10971114
});
10981115
});
10991116

11001117
// Private for unit tests
11011118
exports._EXTENSION_MAP_PREF = _EXTENSION_MAP_PREF;
11021119
exports._NAME_MAP_PREF = _NAME_MAP_PREF;
1103-
exports._resetLanguageOverrides = _resetLanguageOverrides;
1104-
// Internal use only
1105-
// _setLanguageOverrideForPath is used by Document to help LanguageManager keeping track of
1106-
// in-document language overrides
1107-
exports._setLanguageOverrideForPath = _setLanguageOverrideForPath;
1120+
exports._resetPathLanguageOverrides = _resetPathLanguageOverrides;
11081121

11091122
// Public methods
11101123
exports.ready = _ready;
@@ -1113,4 +1126,5 @@ define(function (require, exports, module) {
11131126
exports.getLanguageForExtension = getLanguageForExtension;
11141127
exports.getLanguageForPath = getLanguageForPath;
11151128
exports.getLanguages = getLanguages;
1129+
exports.setLanguageOverrideForPath = setLanguageOverrideForPath;
11161130
});

src/nls/root/strings.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ define({
252252
"STATUSBAR_LANG_TOOLTIP" : "Click to change file type",
253253
"STATUSBAR_CODE_INSPECTION_TOOLTIP" : "{0}. Click to toggle report panel.",
254254
"STATUSBAR_DEFAULT_LANG" : "(default)",
255+
"STATUSBAR_SET_DEFAULT_LANG" : "Set as Default for .{0} Files",
255256

256257
// CodeInspection: errors/warnings
257258
"ERRORS_PANEL_TITLE_MULTIPLE" : "{0} Problems",

src/project/ProjectManager.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,8 +1175,8 @@ define(function (require, exports, module) {
11751175
}
11761176

11771177
/**
1178-
* Loads the given folder as a project. Normally, you would call openProject() instead to let the
1179-
* user choose a folder.
1178+
* Loads the given folder as a project. Does NOT prompt about any unsaved changes - use openProject()
1179+
* instead to check for unsaved changes and (optionally) let the user choose the folder to open.
11801180
*
11811181
* @param {!string} rootPath Absolute path to the root folder of the project.
11821182
* A trailing "/" on the path is optional (unlike many Brackets APIs that assume a trailing "/").
@@ -1213,8 +1213,9 @@ define(function (require, exports, module) {
12131213
DocumentManager.closeAll();
12141214

12151215
_unwatchProjectRoot().always(function () {
1216-
// Done closing old project (if any)
1216+
// Finish closing old project (if any)
12171217
if (_projectRoot) {
1218+
LanguageManager._resetPathLanguageOverrides();
12181219
PreferencesManager._reloadUserPrefs(_projectRoot);
12191220
$(exports).triggerHandler("projectClose", _projectRoot);
12201221
}

0 commit comments

Comments
 (0)