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

Correctly selects filename when known extension with a dot inside is used #7242

Merged
merged 4 commits into from
Mar 24, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/file/FileUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down Expand Up @@ -309,6 +310,40 @@ 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();
}

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 {
break;
}
}
return extension.join(".");
}

/**
* Computes filename as relative to the basePath. For example:
* basePath: /foo/bar/, filename: /foo/bar/baz.txt
Expand Down Expand Up @@ -426,5 +461,6 @@ define(function (require, exports, module) {
exports.getBaseName = getBaseName;
exports.getRelativeFilename = getRelativeFilename;
exports.getFileExtension = getFileExtension;
exports.getSmartFileExtension = getSmartFileExtension;
exports.compareFilenames = compareFilenames;
});
10 changes: 10 additions & 0 deletions src/language/LanguageManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -804,5 +813,6 @@ define(function (require, exports, module) {
exports.ready = _ready;
exports.defineLanguage = defineLanguage;
exports.getLanguage = getLanguage;
exports.getLanguageForExtension = getLanguageForExtension;
exports.getLanguageForPath = getLanguageForPath;
});
10 changes: 7 additions & 3 deletions src/project/ProjectManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -1810,9 +1810,13 @@ define(function (require, exports, module) {
var escapedName = _.escape(entry.name);
_projectTree.jstree("set_text", $selected, escapedName);
_projectTree.jstree("rename");
var indexOfExtension = escapedName.lastIndexOf('.');
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
Expand Down
6 changes: 4 additions & 2 deletions src/utils/ViewUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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.
Expand Down
40 changes: 40 additions & 0 deletions test/spec/FileUtils-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,5 +125,45 @@ 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");
});

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");
});
});
});
});