From 454991485979eba66662d075bcf2341c656b3738 Mon Sep 17 00:00:00 2001 From: Martin Zagora Date: Wed, 19 Mar 2014 08:47:00 +1100 Subject: [PATCH 1/4] Correctly selects filename when known extension with a dot inside is used --- src/project/ProjectManager.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/project/ProjectManager.js b/src/project/ProjectManager.js index 16fe5bd7f2e..5d2ce66f9c6 100644 --- a/src/project/ProjectManager.js +++ b/src/project/ProjectManager.js @@ -1810,7 +1810,21 @@ define(function (require, exports, module) { var escapedName = _.escape(entry.name); _projectTree.jstree("set_text", $selected, escapedName); _projectTree.jstree("rename"); - var indexOfExtension = escapedName.lastIndexOf('.'); + + var indexOfExtension = escapedName.lastIndexOf("."), + language = LanguageManager.getLanguageForPath(entry.name); + if (language) { + language.getFileExtensions().forEach(function (ext) { + ext = "." + ext; + if (escapedName.match(ext + "$")) { + var io = escapedName.lastIndexOf(ext); + if (io < indexOfExtension) { + indexOfExtension = io; + } + } + }); + } + if (indexOfExtension > 0) { $selected.children(".jstree-rename-input")[0].setSelectionRange(0, indexOfExtension); } From 254b01e2f2eebea4416026d0f40d017b8ca6dbc9 Mon Sep 17 00:00:00 2001 From: Martin Zagora Date: Fri, 21 Mar 2014 14:34:34 +1100 Subject: [PATCH 2/4] Refactor to create a new FileUtils method --- src/file/FileUtils.js | 37 +++++++++++++++++++++++++++++++++ src/language/LanguageManager.js | 10 +++++++++ src/project/ProjectManager.js | 22 ++++++-------------- test/spec/FileUtils-test.js | 34 ++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 16 deletions(-) diff --git a/src/file/FileUtils.js b/src/file/FileUtils.js index 55c418f4fa5..9697becd8b0 100644 --- a/src/file/FileUtils.js +++ b/src/file/FileUtils.js @@ -34,6 +34,7 @@ define(function (require, exports, module) { require("utils/Global"); var FileSystemError = require("filesystem/FileSystemError"), + LanguageManager = require("language/LanguageManager"), PerfUtils = require("utils/PerfUtils"), Dialogs = require("widgets/Dialogs"), DefaultDialogs = require("widgets/DefaultDialogs"), @@ -309,6 +310,41 @@ define(function (require, exports, module) { return baseName.substr(idx + 1); } + /** + * Get the file extension (excluding ".") given a path OR a bare filename. + * Returns "" for names with no extension. + * If the only `.` in the file is the first character, + * returns "" as this is not considered an extension. + * This method considers known extensions which include `.` in them. + * + * @param {string} fullPath full path to a file or directory + * @return {string} Returns the extension of a filename or empty string if + * the argument is a directory or a filename with no extension + */ + function getSmartFileExtension(fullPath) { + var baseName = getBaseName(fullPath), + parts = baseName.split("."); + + // get rid of file name + parts.shift(); + if (baseName[0] === ".") { + // if starts with a `.`, then still consider it as file name + parts.shift(); + } + + // test all other parts of the baseName + while (parts.length > 1) { + var ext = parts.join("."); + if (LanguageManager.getLanguageForExtension(ext)) { + return ext; + } else { + parts.shift(); + } + } + + return parts[0] || ""; + } + /** * Computes filename as relative to the basePath. For example: * basePath: /foo/bar/, filename: /foo/bar/baz.txt @@ -426,5 +462,6 @@ define(function (require, exports, module) { exports.getBaseName = getBaseName; exports.getRelativeFilename = getRelativeFilename; exports.getFileExtension = getFileExtension; + exports.getSmartFileExtension = getSmartFileExtension; exports.compareFilenames = compareFilenames; }); diff --git a/src/language/LanguageManager.js b/src/language/LanguageManager.js index 5b06397b57f..db6a7a83abb 100644 --- a/src/language/LanguageManager.js +++ b/src/language/LanguageManager.js @@ -196,6 +196,15 @@ define(function (require, exports, module) { return _languages[id]; } + /** + * Resolves a language to a file extension + * @param {!string} extension Extension that language should be resolved for + * @return {?Language} The language for the provided extension or null if none exists + */ + function getLanguageForExtension(extension) { + return _fileExtensionToLanguageMap[extension.toLowerCase()]; + } + /** * Resolves a file path to a Language object. * @param {!string} path Path to the file to find a language for @@ -804,5 +813,6 @@ define(function (require, exports, module) { exports.ready = _ready; exports.defineLanguage = defineLanguage; exports.getLanguage = getLanguage; + exports.getLanguageForExtension = getLanguageForExtension; exports.getLanguageForPath = getLanguageForPath; }); diff --git a/src/project/ProjectManager.js b/src/project/ProjectManager.js index 5d2ce66f9c6..49cd2279e32 100644 --- a/src/project/ProjectManager.js +++ b/src/project/ProjectManager.js @@ -1811,22 +1811,12 @@ define(function (require, exports, module) { _projectTree.jstree("set_text", $selected, escapedName); _projectTree.jstree("rename"); - var indexOfExtension = escapedName.lastIndexOf("."), - language = LanguageManager.getLanguageForPath(entry.name); - if (language) { - language.getFileExtensions().forEach(function (ext) { - ext = "." + ext; - if (escapedName.match(ext + "$")) { - var io = escapedName.lastIndexOf(ext); - if (io < indexOfExtension) { - indexOfExtension = io; - } - } - }); - } - - if (indexOfExtension > 0) { - $selected.children(".jstree-rename-input")[0].setSelectionRange(0, indexOfExtension); + var extension = FileUtils.getSmartFileExtension(entry.name); + if (extension) { + var indexOfExtension = escapedName.length - extension.length - 1; + if (indexOfExtension > 0) { + $selected.children(".jstree-rename-input")[0].setSelectionRange(0, indexOfExtension); + } } }); // No fail handler: silently no-op if file doesn't exist in tree diff --git a/test/spec/FileUtils-test.js b/test/spec/FileUtils-test.js index 457188ceea5..2b8e4861c55 100644 --- a/test/spec/FileUtils-test.js +++ b/test/spec/FileUtils-test.js @@ -125,5 +125,39 @@ define(function (require, exports, module) { expect(FileUtils.getFileExtension("foo.bar.baz..jaz.txt")).toBe("txt"); }); }); + + describe("getSmartFileExtension", function () { + + it("should get the extension of a normalized win file path", function () { + expect(FileUtils.getSmartFileExtension("C:/foo/bar/baz.txt")).toBe("txt"); + }); + + it("should get the extension of a posix file path", function () { + expect(FileUtils.getSmartFileExtension("/foo/bar/baz.txt")).toBe("txt"); + }); + + it("should return empty extension for a normalized win directory path", function () { + expect(FileUtils.getSmartFileExtension("C:/foo/bar/")).toBe(""); + }); + + it("should return empty extension for a posix directory path", function () { + expect(FileUtils.getSmartFileExtension("bar")).toBe(""); + }); + + it("should return the extension of a filename containing .", function () { + expect(FileUtils.getSmartFileExtension("C:/foo/bar/.baz/jaz.txt")).toBe("txt"); + expect(FileUtils.getSmartFileExtension("foo/bar/baz/.jaz.txt")).toBe("txt"); + expect(FileUtils.getSmartFileExtension("foo.bar.baz..jaz.txt")).toBe("txt"); + }); + + it("should return no extension for files with only . as a first character", function () { + expect(FileUtils.getSmartFileExtension("C:/foo/bar/.baz/.jaz")).toBe(""); + }); + + it("should return the extension containing . for known types", function () { + expect(FileUtils.getSmartFileExtension("C:/foo/bar/.baz/jaz.scss.erb")).toBe("scss.erb"); + expect(FileUtils.getSmartFileExtension("foo/bar/baz/.jaz.js.erb")).toBe("js.erb"); + }); + }); }); }); From b39175ce4d77148044c26d184ca4903ac9d3f2b3 Mon Sep 17 00:00:00 2001 From: Martin Zagora Date: Sat, 22 Mar 2014 12:05:36 +1100 Subject: [PATCH 3/4] Fixed according to comments and added tests for new cases --- src/file/FileUtils.js | 15 +++++++-------- test/spec/FileUtils-test.js | 6 ++++++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/file/FileUtils.js b/src/file/FileUtils.js index 9697becd8b0..cf6648a0841 100644 --- a/src/file/FileUtils.js +++ b/src/file/FileUtils.js @@ -332,17 +332,16 @@ define(function (require, exports, module) { parts.shift(); } - // test all other parts of the baseName - while (parts.length > 1) { - var ext = parts.join("."); - if (LanguageManager.getLanguageForExtension(ext)) { - return ext; + var extension = [parts.pop()], // last part is always an extension + i = parts.length; + while (i--) { + if (LanguageManager.getLanguageForExtension(parts[i])) { + extension.unshift(parts[i]); } else { - parts.shift(); + break; } } - - return parts[0] || ""; + return extension.join("."); } /** diff --git a/test/spec/FileUtils-test.js b/test/spec/FileUtils-test.js index 2b8e4861c55..11948bc62a0 100644 --- a/test/spec/FileUtils-test.js +++ b/test/spec/FileUtils-test.js @@ -158,6 +158,12 @@ define(function (require, exports, module) { expect(FileUtils.getSmartFileExtension("C:/foo/bar/.baz/jaz.scss.erb")).toBe("scss.erb"); expect(FileUtils.getSmartFileExtension("foo/bar/baz/.jaz.js.erb")).toBe("js.erb"); }); + + it("should return the extension combined from other known extensions", function () { + expect(FileUtils.getSmartFileExtension("foo.bar.php.js")).toBe("php.js"); + expect(FileUtils.getSmartFileExtension("foo.bar.php.html.js")).toBe("php.html.js"); + expect(FileUtils.getSmartFileExtension("foo.bar.php.scss.erb")).toBe("php.scss.erb"); + }); }); }); }); From 4b501a8d35e85e66fe01e70ebbcaa7dde8de3d0c Mon Sep 17 00:00:00 2001 From: Martin Zagora Date: Tue, 25 Mar 2014 08:36:11 +1100 Subject: [PATCH 4/4] Extensions are now displayed using new API in project tree --- src/utils/ViewUtils.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/utils/ViewUtils.js b/src/utils/ViewUtils.js index ff4b213a6d1..0c12192e75c 100644 --- a/src/utils/ViewUtils.js +++ b/src/utils/ViewUtils.js @@ -28,7 +28,8 @@ define(function (require, exports, module) { "use strict"; - var _ = require("thirdparty/lodash"); + var _ = require("thirdparty/lodash"), + FileUtils = require("file/FileUtils"); var SCROLL_SHADOW_HEIGHT = 5; @@ -393,7 +394,8 @@ define(function (require, exports, module) { */ function getFileEntryDisplay(entry) { var name = entry.name, - i = name.lastIndexOf("."); + ext = FileUtils.getSmartFileExtension(name), + i = name.lastIndexOf("." + ext); if (i >= 0) { // Escape all HTML-sensitive characters in filename.