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

Commit 4fafc1e

Browse files
committed
Merge pull request #3029 from MarkMurphy/markmurphy/issue-2923
Fixing issue #2923 - Getting mode from file extension won't always work
2 parents c64b442 + 76ac3ff commit 4fafc1e

File tree

8 files changed

+85
-31
lines changed

8 files changed

+85
-31
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,7 @@ src/extensions/disabled
1919

2020
# unit test working directory
2121
test/temp
22-
test/results
22+
test/results
23+
24+
# Netbeans
25+
/nbproject

src/document/DocumentManager.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -952,8 +952,7 @@ define(function (require, exports, module) {
952952
*/
953953
Document.prototype._updateLanguage = function () {
954954
var oldLanguage = this.language;
955-
var ext = PathUtils.filenameExtension(this.file.fullPath);
956-
this.language = LanguageManager.getLanguageForFileExtension(ext);
955+
this.language = LanguageManager.getLanguageForPath(this.file.fullPath);
957956

958957
if (oldLanguage && oldLanguage !== this.language) {
959958
$(this).triggerHandler("languageChanged", [oldLanguage, this.language]);

src/editor/Editor.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,7 +1224,7 @@ define(function (require, exports, module) {
12241224
*
12251225
* @return {?(Object|string)} Name of syntax-highlighting mode, or object containing a "name" property
12261226
* naming the mode along with configuration options required by the mode.
1227-
* See {@link LanguageManager#getLanguageForFileExtension()} and {@link Language#getMode()}.
1227+
* See {@link LanguageManager#getLanguageForPath()} and {@link Language#getMode()}.
12281228
*/
12291229
Editor.prototype.getModeForSelection = function () {
12301230
// Check for mixed mode info
@@ -1257,7 +1257,7 @@ define(function (require, exports, module) {
12571257
/**
12581258
* Gets the syntax-highlighting mode for the document.
12591259
*
1260-
* @return {Object|String} Object or Name of syntax-highlighting mode; see {@link LanguageManager#getLanguageForFileExtension()} and {@link Language#getMode()}.
1260+
* @return {Object|String} Object or Name of syntax-highlighting mode; see {@link LanguageManager#getLanguageForPath()} and {@link Language#getMode()}.
12611261
*/
12621262
Editor.prototype.getModeForDocument = function () {
12631263
return this._codeMirror.getOption("mode");

src/extensions/default/JavaScriptCodeHints/ScopeManager.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ define(function (require, exports, module) {
440440
file = split.file;
441441

442442
if (file.indexOf(".") > 1) { // ignore /.dotfiles
443-
var mode = LanguageManager.getLanguageForFileExtension(entry.fullPath).getMode();
443+
var mode = LanguageManager.getLanguageForPath(entry.fullPath).getMode();
444444
if (mode === HintUtils.MODE_NAME) {
445445
DocumentManager.getDocumentForPath(path).done(function (document) {
446446
refreshOuterScope(dir, file, document.getText());

src/language/LanguageManager.js

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ define(function (require, exports, module) {
9595
_pendingLanguages = {},
9696
_languages = {},
9797
_fileExtensionToLanguageMap = {},
98+
_fileNameToLanguageMap = {},
9899
_modeToLanguageMap = {},
99100
_ready;
100101

@@ -178,16 +179,19 @@ define(function (require, exports, module) {
178179
}
179180

180181
/**
181-
* Resolves a file extension to a Language object.
182-
* @param {!string} path Path to or extension of the file to find a language for
182+
* Resolves a file path to a Language object.
183+
* @param {!string} path Path to the file to find a language for
183184
* @return {Language} The language for the provided file type or the fallback language
184185
*/
185-
function getLanguageForFileExtension(path) {
186+
function getLanguageForPath(path) {
186187
var extension = _normalizeFileExtension(PathUtils.filenameExtension(path)),
187-
language = _fileExtensionToLanguageMap[extension];
188+
filename = PathUtils.filename(path),
189+
language = extension ? _fileExtensionToLanguageMap[extension]
190+
: _fileNameToLanguageMap[filename];
188191

189192
if (!language) {
190-
console.log("Called LanguageManager.getLanguageForFileExtension with an unhandled file extension:", extension);
193+
extension ? console.log("Called LanguageManager.getLanguageForPath with an unhandled file extension:", extension)
194+
: console.log("Called LanguageManager.getLanguageForPath with an unhandled file name:", filename);
191195
}
192196

193197
return language || _fallbackLanguage;
@@ -233,6 +237,7 @@ define(function (require, exports, module) {
233237
this._name = name;
234238

235239
this._fileExtensions = [];
240+
this._fileNames = [];
236241
this._modeToLanguageMap = {};
237242
}
238243

@@ -249,6 +254,9 @@ define(function (require, exports, module) {
249254
/** @type {Array.<string>} File extensions that use this language */
250255
Language.prototype._fileExtensions = null;
251256

257+
/** @type {Array.<string>} File names for extensionless files that use this language */
258+
Language.prototype._fileNames = null;
259+
252260
/** @type {{ prefix: string }} Line comment syntax */
253261
Language.prototype._lineCommentSyntax = null;
254262

@@ -351,6 +359,15 @@ define(function (require, exports, module) {
351359
// Use concat to create a copy of this array, preventing external modification
352360
return this._fileExtensions.concat();
353361
};
362+
363+
/**
364+
* Returns an array of file names for extensionless files that use this language.
365+
* @return {Array.<string>} Extensionless file names used by this language
366+
*/
367+
Language.prototype.getFileNames = function () {
368+
// Use concat to create a copy of this array, preventing external modification
369+
return this._fileNames.concat();
370+
};
354371

355372
/**
356373
* Adds a file extension to this language.
@@ -379,6 +396,31 @@ define(function (require, exports, module) {
379396
}
380397
};
381398

399+
/**
400+
* Adds a file name to the language which is used to match files that don't have extensions like "Makefile" for example.
401+
* Private for now since dependent code would need to by kept in sync with such changes.
402+
* See https://github.com/adobe/brackets/issues/2966 for plans to make this public.
403+
* @param {!string} extension An extensionless file name used by this language
404+
* @private
405+
*/
406+
Language.prototype._addFileName = function (name) {
407+
if (this._fileNames.indexOf(name) === -1) {
408+
this._fileNames.push(name);
409+
410+
var language = _fileNameToLanguageMap[name];
411+
if (language) {
412+
console.warn("Cannot register file name \"" + name + "\" for " + this._name + ", it already belongs to " + language._name);
413+
} else {
414+
_fileNameToLanguageMap[name] = this;
415+
416+
// TODO (issue #2966) Allow extensions to add new file names to existing languages
417+
// Notify on the Language and on LanguageManager?
418+
// $(this).triggerHandler("fileNameAdded", [name]);
419+
// $(exports).triggerHandler("fileNameAdded", [name, this]);
420+
}
421+
}
422+
};
423+
382424
/**
383425
* Returns whether the line comment syntax is defined for this language.
384426
* @return {boolean} Whether line comments are supported
@@ -504,6 +546,8 @@ define(function (require, exports, module) {
504546

505547
var language = new Language(id, definition.name),
506548
fileExtensions = definition.fileExtensions,
549+
fileNames = definition.fileNames,
550+
l,
507551
i;
508552

509553
var blockComment = definition.blockComment;
@@ -522,10 +566,17 @@ define(function (require, exports, module) {
522566
language._loadAndSetMode(definition.mode).done(function () {
523567
// register language file extensions after mode has loaded
524568
if (fileExtensions) {
525-
for (i = 0; i < fileExtensions.length; i++) {
569+
for (i = 0, l = fileExtensions.length; i < l; i++) {
526570
language._addFileExtension(fileExtensions[i]);
527571
}
528572
}
573+
574+
// register language file names after mode has loaded
575+
if (fileNames) {
576+
for (i = 0, l = fileNames.length; i < l; i++) {
577+
language._addFileName(fileNames[i]);
578+
}
579+
}
529580

530581
// globally associate mode to language
531582
_setLanguageForMode(language.getMode(), language);
@@ -581,8 +632,8 @@ define(function (require, exports, module) {
581632
});
582633

583634
// Public methods
584-
exports.ready = _ready;
585-
exports.defineLanguage = defineLanguage;
586-
exports.getLanguage = getLanguage;
587-
exports.getLanguageForFileExtension = getLanguageForFileExtension;
635+
exports.ready = _ready;
636+
exports.defineLanguage = defineLanguage;
637+
exports.getLanguage = getLanguage;
638+
exports.getLanguageForPath = getLanguageForPath;
588639
});

src/language/languages.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"xml": {
3636
"name": "XML",
3737
"mode": "xml",
38-
"fileExtensions": ["svg", "xml", "wxs", "wxl"],
38+
"fileExtensions": ["svg", "xml", "wxs", "wxl", "wsdl", "rss", "atom", "rdf", "xslt", "xul", "xbl", "mathml"],
3939
"blockComment": ["<!--", "-->"]
4040
},
4141

@@ -85,7 +85,8 @@
8585
"coffeescript": {
8686
"name": "CoffeeScript",
8787
"mode": "coffeescript",
88-
"fileExtensions": ["coffee"]
88+
"fileExtensions": ["coffee", "cf", "cson"],
89+
"fileNames": ["Cakefile"]
8990
},
9091

9192
"clojure": {
@@ -103,7 +104,7 @@
103104
"ruby": {
104105
"name": "Ruby",
105106
"mode": "ruby",
106-
"fileExtensions": ["rb"]
107+
"fileExtensions": ["rb", "ru", "gemspec", "rake"]
107108
},
108109

109110
"python": {
@@ -134,7 +135,7 @@
134135
"name": "Diff",
135136
"mode": "diff",
136137
"fileExtensions": ["diff", "patch"]
137-
},
138+
},
138139

139140
"markdown": {
140141
"name": "Markdown",

test/spec/Editor-test.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,25 +117,25 @@ define(function (require, exports, module) {
117117

118118
it("should switch to the HTML mode for files ending in .html", function () {
119119
// verify editor content
120-
var mode = LanguageManager.getLanguageForFileExtension("file:///only/testing/the/path.html").getMode();
120+
var mode = LanguageManager.getLanguageForPath("file:///only/testing/the/path.html").getMode();
121121
expect(mode).toSpecifyModeNamed("text/x-brackets-html");
122122
});
123123

124124
it("should switch modes even if the url has a query string", function () {
125125
// verify editor content
126-
var mode = LanguageManager.getLanguageForFileExtension("http://only.org/testing/the/path.css?v=2").getMode();
126+
var mode = LanguageManager.getLanguageForPath("http://only.org/testing/the/path.css?v=2").getMode();
127127
expect(mode).toSpecifyModeNamed(langNames.css.mode);
128128
});
129129

130130
it("should accept just a file name too", function () {
131131
// verify editor content
132-
var mode = LanguageManager.getLanguageForFileExtension("path.js").getMode();
132+
var mode = LanguageManager.getLanguageForPath("path.js").getMode();
133133
expect(mode).toSpecifyModeNamed(langNames.javascript.mode);
134134
});
135135

136136
it("should default to plain text for unknown file extensions", function () {
137137
// verify editor content
138-
var mode = LanguageManager.getLanguageForFileExtension("test.foo").getMode();
138+
var mode = LanguageManager.getLanguageForPath("test.foo").getMode();
139139

140140
// "unknown" mode uses it's MIME type instead
141141
expect(mode).toBe("text/plain");

test/spec/LanguageManager-test.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,9 @@ define(function (require, exports, module) {
120120
var html = LanguageManager.getLanguage("html"),
121121
unknown = LanguageManager.getLanguage("unknown");
122122

123-
expect(LanguageManager.getLanguageForFileExtension("foo.html")).toBe(html);
124-
expect(LanguageManager.getLanguageForFileExtension("INDEX.HTML")).toBe(html);
125-
expect(LanguageManager.getLanguageForFileExtension("foo.doesNotExist")).toBe(unknown);
123+
expect(LanguageManager.getLanguageForPath("foo.html")).toBe(html);
124+
expect(LanguageManager.getLanguageForPath("INDEX.HTML")).toBe(html);
125+
expect(LanguageManager.getLanguageForPath("foo.doesNotExist")).toBe(unknown);
126126
});
127127

128128
});
@@ -176,7 +176,7 @@ define(function (require, exports, module) {
176176
}, "The language should be resolved", 50);
177177

178178
runs(function () {
179-
expect(LanguageManager.getLanguageForFileExtension("file.p")).toBe(language);
179+
expect(LanguageManager.getLanguageForPath("file.p")).toBe(language);
180180
validateLanguage(def, language);
181181
});
182182
});
@@ -202,8 +202,8 @@ define(function (require, exports, module) {
202202

203203
runs(function () {
204204
expect(xmlBefore).toBe(xmlAfter);
205-
expect(LanguageManager.getLanguageForFileExtension("file.wix")).toBe(lang);
206-
expect(LanguageManager.getLanguageForFileExtension("file.xml")).toBe(xmlAfter);
205+
expect(LanguageManager.getLanguageForPath("file.wix")).toBe(lang);
206+
expect(LanguageManager.getLanguageForPath("file.xml")).toBe(xmlAfter);
207207

208208
validateLanguage(def, lang);
209209
});
@@ -272,7 +272,7 @@ define(function (require, exports, module) {
272272
}, "The language should be resolved", 50);
273273

274274
runs(function () {
275-
expect(LanguageManager.getLanguageForFileExtension("file.erlang")).toBe(language);
275+
expect(LanguageManager.getLanguageForPath("file.erlang")).toBe(language);
276276
validateLanguage(def, language);
277277
});
278278

0 commit comments

Comments
 (0)