From 247d711e05af232064ad7e7d7d0b3c87fdb133af Mon Sep 17 00:00:00 2001 From: Peter Flynn Date: Fri, 21 Mar 2014 15:16:35 -0700 Subject: [PATCH 1/4] A few small tweaks to language mappings: * Recognize a wider variety of HTML templates embedded in script tags * Remove generic "clike" language declaration that no files map to - seems unneeded, and clutters the dropdown in PR #6409 * Add missing commenting syntax to Scala language * Automatically filter out a variety of common binary file types from Find in Files (it already checks isBinary) - note that this does not affect Mac, where all binary file IO fails automatically. Helps reduce bugs like #6091. Leaves the ProjectManager.isBinary() API in place for now, since it also excludes files from Quick Open and other getAllFiles() clients that don't yet check isBinary via LanguageManager. * Fix incorrect code in Find in Files that only narrowly avoids creating bugs where filtered-out files are incorrectly added to search results. --- src/language/LanguageManager.js | 4 ++-- src/language/languages.json | 18 ++++++++++-------- src/search/FindInFiles.js | 4 +++- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/language/LanguageManager.js b/src/language/LanguageManager.js index 5b06397b57f..97be528f6d2 100644 --- a/src/language/LanguageManager.js +++ b/src/language/LanguageManager.js @@ -770,11 +770,11 @@ define(function (require, exports, module) { _patchCodeMirror(); // Define a custom MIME mode here instead of putting it directly into languages.json - // because JSON files must not contain regular expressions. Also, all other modes so + // because JSON files can't contain regular expressions. Also, all other modes so // far were strings, so we spare us the trouble of allowing more complex mode values. CodeMirror.defineMIME("text/x-brackets-html", { "name": "htmlmixed", - "scriptTypes": [{"matches": /\/x-handlebars-template|\/x-mustache/i, + "scriptTypes": [{"matches": /\/x-handlebars|\/x-mustache|^text\/html$/i, "mode": null}] }); diff --git a/src/language/languages.json b/src/language/languages.json index d1d17992b16..136fb1edeab 100644 --- a/src/language/languages.json +++ b/src/language/languages.json @@ -120,13 +120,6 @@ "lineComment": ["//"] }, - "clike": { - "name": "clike", - "mode": "clike", - "blockComment": ["/*", "*/"], - "lineComment": ["//", "#"] - }, - "java": { "name": "Java", "mode": ["clike", "text/x-java"], @@ -138,7 +131,9 @@ "scala": { "name": "Scala", "mode": ["clike", "text/x-scala"], - "fileExtensions": ["scala", "sbt"] + "fileExtensions": ["scala", "sbt"], + "blockComment": ["/*", "*/"], + "lineComment": ["//"] }, "coffeescript": { @@ -242,5 +237,12 @@ "name": "Audio", "fileExtensions": ["mp3", "wav", "aif", "aiff", "ogg"], "isBinary": true + }, + + "binary": { + "name": "Other Binary", + "fileExtensions": ["svgz", "jsz", "zip", "gz", "htmz", "htmlz", "rar", "tar", "exe", "bin", "dll", "pdb", "lib", "obj", "so", "a", "dylib", + "pdf", "psd", "ai", "tif", "tiff", "mpg", "mpeg", "avi", "flv", "mp4", "ttf", "otf", "woff"], + "isBinary": true } } diff --git a/src/search/FindInFiles.js b/src/search/FindInFiles.js index a40c011f21f..376917419d3 100644 --- a/src/search/FindInFiles.js +++ b/src/search/FindInFiles.js @@ -751,7 +751,9 @@ define(function (require, exports, module) { var inWorkingSet = DocumentManager.getWorkingSet().some(function (wsFile) { return wsFile.fullPath === file.fullPath; }); - return inWorkingSet; + if (!inWorkingSet) { + return false; + } } } // In the initial search, this is passed as a getAllFiles() filter From 78ac6eaaeaceef4b2456e81af53b6b58b11adfe3 Mon Sep 17 00:00:00 2001 From: Peter Flynn Date: Sun, 23 Mar 2014 23:03:30 -0700 Subject: [PATCH 2/4] - Ability to remove file extension/name mappings from existing Language (#6873) - with unit tests. - Remove inaccurate docs on Language add extension/name APIs - Fix PHP mixed-mode handling so token-level detection (getLanguageForMode()) works - similar to our handling of "htmlmixed" mode. Allows us to remove "clike" dummy language without breaking PHP Toggle Comment functionality --- src/language/LanguageManager.js | 48 +++++++++++++++++++++++++++++-- src/language/languages.json | 4 ++- test/spec/LanguageManager-test.js | 28 ++++++++++++++++++ 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/src/language/LanguageManager.js b/src/language/LanguageManager.js index 97be528f6d2..744ba429572 100644 --- a/src/language/LanguageManager.js +++ b/src/language/LanguageManager.js @@ -468,7 +468,6 @@ define(function (require, exports, module) { /** * Adds a file extension to this language. * @param {!string} extension A file extension used by this language - * @return {boolean} Whether adding the file extension was successful or not */ Language.prototype.addFileExtension = function (extension) { // Remove a leading dot if present @@ -493,10 +492,32 @@ define(function (require, exports, module) { } }; + /** + * Unregisters a file extension from this language. + * @param {!string} extension File extension to stop using for this language + */ + Language.prototype.removeFileExtension = function (extension) { + // Remove a leading dot if present + if (extension.charAt(0) === ".") { + extension = extension.substr(1); + } + + // Make checks below case-INsensitive + extension = extension.toLowerCase(); + + var index = this._fileExtensions.indexOf(extension); + if (index !== -1) { + this._fileExtensions.splice(index, 1); + + delete _fileExtensionToLanguageMap[extension]; + + this._wasModified(); + } + }; + /** * Adds a file name to the language which is used to match files that don't have extensions like "Makefile" for example. * @param {!string} extension An extensionless file name used by this language - * @return {boolean} Whether adding the file name was successful or not */ Language.prototype.addFileName = function (name) { // Make checks below case-INsensitive @@ -514,7 +535,24 @@ define(function (require, exports, module) { this._wasModified(); } - return true; + }; + + /** + * Unregisters a file name from this language. + * @param {!string} extension An extensionless file name used by this language + */ + Language.prototype.removeFileName = function (name) { + // Make checks below case-INsensitive + name = name.toLowerCase(); + + var index = this._fileNames.indexOf(name); + if (index !== -1) { + this._fileNames.splice(index, 1); + + delete _fileNameToLanguageMap[name]; + + this._wasModified(); + } }; /** @@ -796,6 +834,10 @@ define(function (require, exports, module) { // But for now, we need to associate this madeup "html" mode with our HTML language object. _setLanguageForMode("html", html); + // Similarly, the php mode uses clike internally for the PHP parts + var php = getLanguage("php"); + php._setLanguageForMode("clike", php); + // The fallback language for unknown modes and file extensions _fallbackLanguage = getLanguage("unknown"); }); diff --git a/src/language/languages.json b/src/language/languages.json index 136fb1edeab..9eb7d8ab194 100644 --- a/src/language/languages.json +++ b/src/language/languages.json @@ -93,7 +93,9 @@ "php": { "name": "PHP", "mode": "php", - "fileExtensions": ["php", "php3", "php4", "php5", "phtm", "phtml", "ctp"] + "fileExtensions": ["php", "php3", "php4", "php5", "phtm", "phtml", "ctp"], + "blockComment": ["/*", "*/"], + "lineComment": ["//", "#"] }, "c": { diff --git a/test/spec/LanguageManager-test.js b/test/spec/LanguageManager-test.js index dbc72545277..7dc4a9a96e2 100644 --- a/test/spec/LanguageManager-test.js +++ b/test/spec/LanguageManager-test.js @@ -181,6 +181,34 @@ define(function (require, exports, module) { expect(LanguageManager.getLanguageForPath("cakefile.doesNotExist")).toBe(unknown); expect(LanguageManager.getLanguageForPath("Something.cakefile")).toBe(unknown); }); + + it("should remove file extensions and add to new languages", function () { + var html = LanguageManager.getLanguage("html"), + ruby = LanguageManager.getLanguage("ruby"), + unknown = LanguageManager.getLanguage("unknown"); + + expect(LanguageManager.getLanguageForPath("test.html")).toBe(html); + + html.removeFileExtension("html"); + expect(LanguageManager.getLanguageForPath("test.html")).toBe(unknown); + + ruby.addFileExtension("html"); + expect(LanguageManager.getLanguageForPath("test.html")).toBe(ruby); + }); + + it("should remove file names and add to new languages", function () { + var coffee = LanguageManager.getLanguage("coffeescript"), + html = LanguageManager.getLanguage("html"), + unknown = LanguageManager.getLanguage("unknown"); + + expect(LanguageManager.getLanguageForPath("Cakefile")).toBe(coffee); + + coffee.removeFileName("Cakefile"); + expect(LanguageManager.getLanguageForPath("Cakefile")).toBe(unknown); + + html.addFileName("Cakefile"); + expect(LanguageManager.getLanguageForPath("Cakefile")).toBe(html); + }); }); describe("defineLanguage", function () { From e9ad8a071631d9f5be48562398ceea0a74484f83 Mon Sep 17 00:00:00 2001 From: Peter Flynn Date: Thu, 27 Mar 2014 16:30:53 -0700 Subject: [PATCH 3/4] Code review: change Language add/remove extension & filename APIs to accept an array of multiple items in addition to a single item as supported before. With unit tests. --- src/language/LanguageManager.js | 44 ++++++++++++++++++++----- test/spec/LanguageManager-test.js | 54 +++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 8 deletions(-) diff --git a/src/language/LanguageManager.js b/src/language/LanguageManager.js index 744ba429572..c859ec9712b 100644 --- a/src/language/LanguageManager.js +++ b/src/language/LanguageManager.js @@ -466,10 +466,17 @@ define(function (require, exports, module) { }; /** - * Adds a file extension to this language. - * @param {!string} extension A file extension used by this language + * Adds one or more file extensions to this language. + * @param {!string|Array.>} extension A file extension (or array thereof) used by this language */ Language.prototype.addFileExtension = function (extension) { + if (Array.isArray(extension)) { + extension.forEach(this._addFileExtension.bind(this)); + } else { + this._addFileExtension(extension); + } + }; + Language.prototype._addFileExtension = function (extension) { // Remove a leading dot if present if (extension.charAt(0) === ".") { extension = extension.substr(1); @@ -493,10 +500,17 @@ define(function (require, exports, module) { }; /** - * Unregisters a file extension from this language. - * @param {!string} extension File extension to stop using for this language + * Unregisters one or more file extensions from this language. + * @param {!string|Array.} extension File extension (or array thereof) to stop using for this language */ Language.prototype.removeFileExtension = function (extension) { + if (Array.isArray(extension)) { + extension.forEach(this._removeFileExtension.bind(this)); + } else { + this._removeFileExtension(extension); + } + }; + Language.prototype._removeFileExtension = function (extension) { // Remove a leading dot if present if (extension.charAt(0) === ".") { extension = extension.substr(1); @@ -516,10 +530,17 @@ define(function (require, exports, module) { }; /** - * Adds a file name to the language which is used to match files that don't have extensions like "Makefile" for example. - * @param {!string} extension An extensionless file name used by this language + * Adds one or more file names to the language which is used to match files that don't have extensions like "Makefile" for example. + * @param {!string|Array.} extension An extensionless file name (or array thereof) used by this language */ Language.prototype.addFileName = function (name) { + if (Array.isArray(name)) { + name.forEach(this._addFileName.bind(this)); + } else { + this._addFileName(name); + } + }; + Language.prototype._addFileName = function (name) { // Make checks below case-INsensitive name = name.toLowerCase(); @@ -538,10 +559,17 @@ define(function (require, exports, module) { }; /** - * Unregisters a file name from this language. - * @param {!string} extension An extensionless file name used by this language + * Unregisters one or more file names from this language. + * @param {!string|Array.} extension An extensionless file name (or array thereof) used by this language */ Language.prototype.removeFileName = function (name) { + if (Array.isArray(name)) { + name.forEach(this._removeFileName.bind(this)); + } else { + this._removeFileName(name); + } + }; + Language.prototype._removeFileName = function (name) { // Make checks below case-INsensitive name = name.toLowerCase(); diff --git a/test/spec/LanguageManager-test.js b/test/spec/LanguageManager-test.js index 7dc4a9a96e2..f66afa8bec1 100644 --- a/test/spec/LanguageManager-test.js +++ b/test/spec/LanguageManager-test.js @@ -209,6 +209,60 @@ define(function (require, exports, module) { html.addFileName("Cakefile"); expect(LanguageManager.getLanguageForPath("Cakefile")).toBe(html); }); + + it("should add multiple file extensions to languages", function () { + var ruby = LanguageManager.getLanguage("ruby"), + unknown = LanguageManager.getLanguage("unknown"); + + expect(LanguageManager.getLanguageForPath("foo.1")).toBe(unknown); + expect(LanguageManager.getLanguageForPath("foo.2")).toBe(unknown); + + ruby.addFileExtension(["1", "2"]); + + expect(LanguageManager.getLanguageForPath("foo.1")).toBe(ruby); + expect(LanguageManager.getLanguageForPath("foo.2")).toBe(ruby); + }); + + it("should remove multiple file extensions from languages", function () { + var ruby = LanguageManager.getLanguage("ruby"), + unknown = LanguageManager.getLanguage("unknown"); + + // Assumes test above already ran (tests in this suite are not isolated) + expect(LanguageManager.getLanguageForPath("foo.1")).toBe(ruby); + expect(LanguageManager.getLanguageForPath("foo.2")).toBe(ruby); + + ruby.removeFileExtension(["1", "2"]); + + expect(LanguageManager.getLanguageForPath("foo.1")).toBe(unknown); + expect(LanguageManager.getLanguageForPath("foo.2")).toBe(unknown); + }); + + it("should add multiple file names to languages", function () { + var ruby = LanguageManager.getLanguage("ruby"), + unknown = LanguageManager.getLanguage("unknown"); + + expect(LanguageManager.getLanguageForPath("rubyFile1")).toBe(unknown); + expect(LanguageManager.getLanguageForPath("rubyFile2")).toBe(unknown); + + ruby.addFileName(["rubyFile1", "rubyFile2"]); + + expect(LanguageManager.getLanguageForPath("rubyFile1")).toBe(ruby); + expect(LanguageManager.getLanguageForPath("rubyFile2")).toBe(ruby); + }); + + it("should remove multiple file names from languages", function () { + var ruby = LanguageManager.getLanguage("ruby"), + unknown = LanguageManager.getLanguage("unknown"); + + // Assumes test above already ran (tests in this suite are not isolated) + expect(LanguageManager.getLanguageForPath("rubyFile1")).toBe(ruby); + expect(LanguageManager.getLanguageForPath("rubyFile2")).toBe(ruby); + + ruby.removeFileName(["rubyFile1", "rubyFile2"]); + + expect(LanguageManager.getLanguageForPath("rubyFile1")).toBe(unknown); + expect(LanguageManager.getLanguageForPath("rubyFile2")).toBe(unknown); + }); }); describe("defineLanguage", function () { From e013ca43aa10c3a804c29775978dce00d36735a8 Mon Sep 17 00:00:00 2001 From: Peter Flynn Date: Fri, 28 Mar 2014 14:27:16 -0700 Subject: [PATCH 4/4] Add some more common binary types: Node native extensions, Flash, EOT webfonts, MS Office files, SQLite db files --- src/language/LanguageManager.js | 2 +- src/language/languages.json | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/language/LanguageManager.js b/src/language/LanguageManager.js index c859ec9712b..586b9fcdadc 100644 --- a/src/language/LanguageManager.js +++ b/src/language/LanguageManager.js @@ -467,7 +467,7 @@ define(function (require, exports, module) { /** * Adds one or more file extensions to this language. - * @param {!string|Array.>} extension A file extension (or array thereof) used by this language + * @param {!string|Array.} extension A file extension (or array thereof) used by this language */ Language.prototype.addFileExtension = function (extension) { if (Array.isArray(extension)) { diff --git a/src/language/languages.json b/src/language/languages.json index 9eb7d8ab194..a229650ee30 100644 --- a/src/language/languages.json +++ b/src/language/languages.json @@ -243,8 +243,9 @@ "binary": { "name": "Other Binary", - "fileExtensions": ["svgz", "jsz", "zip", "gz", "htmz", "htmlz", "rar", "tar", "exe", "bin", "dll", "pdb", "lib", "obj", "so", "a", "dylib", - "pdf", "psd", "ai", "tif", "tiff", "mpg", "mpeg", "avi", "flv", "mp4", "ttf", "otf", "woff"], + "fileExtensions": ["svgz", "jsz", "zip", "gz", "htmz", "htmlz", "rar", "tar", "exe", "bin", "dll", "pdb", "lib", "obj", "o", "so", "a", "dylib", "node", + "pdf", "psd", "ai", "tif", "tiff", "mpg", "mpeg", "avi", "flv", "mp4", "swf", "ttf", "otf", "woff", "eot", + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "sqlite"], "isBinary": true } }