diff --git a/src/LiveDevelopment/LiveDevelopment.js b/src/LiveDevelopment/LiveDevelopment.js index 05195267bcb..72eb05ea64b 100644 --- a/src/LiveDevelopment/LiveDevelopment.js +++ b/src/LiveDevelopment/LiveDevelopment.js @@ -568,7 +568,7 @@ define(function LiveDevelopment(require, exports, module) { // After (1) the interstitial page loads, (2) then browser navigation // to the base URL is completed, and (3) the agents finish loading // gather related documents and finally set status to STATUS_ACTIVE. - var doc = _getCurrentDocument(); // TODO: probably wrong... + var doc = _liveDocument.doc; if (doc) { var status = STATUS_ACTIVE, diff --git a/src/editor/Editor.js b/src/editor/Editor.js index b0822549922..1776c954567 100644 --- a/src/editor/Editor.js +++ b/src/editor/Editor.js @@ -140,10 +140,18 @@ define(function (require, exports, module) { if (indentAuto) { var currentLength = line.length; CodeMirror.commands.indentAuto(instance); - // If the amount of whitespace didn't change, insert another tab + + // If the amount of whitespace and the cursor position didn't change, we must have + // already been at the correct indentation level as far as CM is concerned, so insert + // another tab. if (instance.getLine(from.line).length === currentLength) { - insertTab = true; - to.ch = 0; + var newFrom = instance.getCursor(true), + newTo = instance.getCursor(false); + if (newFrom.line === from.line && newFrom.ch === from.ch && + newTo.line === to.line && newTo.ch === to.ch) { + insertTab = true; + to.ch = 0; + } } } else if (instance.somethingSelected() && from.line !== to.line) { CodeMirror.commands.indentMore(instance); @@ -978,21 +986,10 @@ define(function (require, exports, module) { return; } - // Handle hiding a single blank line at the end specially by moving the "from" backwards - // to include the last newline. Otherwise CodeMirror doesn't hide anything in this case. - var hideFrom, inclusiveLeft = true; - if (from === to - 1 && from === this._codeMirror.lineCount() - 1 && this._codeMirror.getLine(from).length === 0) { - hideFrom = {line: from - 1, ch: this._codeMirror.getLine(from - 1).length}; - // Allow the cursor to be set immediately before the hidden newline. - inclusiveLeft = false; - } else { - hideFrom = {line: from, ch: 0}; - } - var value = this._codeMirror.markText( - hideFrom, + {line: from, ch: 0}, {line: to - 1, ch: this._codeMirror.getLine(to - 1).length}, - {collapsed: true, inclusiveLeft: inclusiveLeft, inclusiveRight: true} + {collapsed: true, inclusiveLeft: true, inclusiveRight: true} ); return value; diff --git a/src/editor/ImageViewer.js b/src/editor/ImageViewer.js index a32fff31c45..39954c2db4b 100644 --- a/src/editor/ImageViewer.js +++ b/src/editor/ImageViewer.js @@ -98,12 +98,12 @@ define(function (require, exports, module) { $("#img-preview").on("load", function () { // add dimensions and size _naturalWidth = this.naturalWidth; - var dimensionString = _naturalWidth + " x " + this.naturalHeight + " " + Strings.UNIT_PIXELS; + var dimensionString = _naturalWidth + " × " + this.naturalHeight + " " + Strings.UNIT_PIXELS; // get image size var file = FileSystem.getFileForPath(fullPath); file.stat(function (err, stat) { if (err) { - $("#img-data").text(dimensionString); + $("#img-data").html(dimensionString); } else { var sizeString = ""; if (stat.size) { diff --git a/src/extensions/default/QuickView/main.js b/src/extensions/default/QuickView/main.js index 36a6d219a81..7deef06e46c 100644 --- a/src/extensions/default/QuickView/main.js +++ b/src/extensions/default/QuickView/main.js @@ -450,7 +450,7 @@ define(function (require, exports, module) { $previewContainer.find(".image-preview > img").on("load", function () { $previewContent .append("
" + - this.naturalWidth + " x " + this.naturalHeight + " " + Strings.UNIT_PIXELS + + this.naturalWidth + " × " + this.naturalHeight + " " + Strings.UNIT_PIXELS + "
" ); $previewContainer.show(); diff --git a/src/extensions/default/UrlCodeHints/main.js b/src/extensions/default/UrlCodeHints/main.js index ec88e0d178e..af33eba7be5 100644 --- a/src/extensions/default/UrlCodeHints/main.js +++ b/src/extensions/default/UrlCodeHints/main.js @@ -139,9 +139,10 @@ define(function (require, exports, module) { // convert to doc relative path var entryStr = entry.fullPath.replace(docDir, ""); - // code hints show the same strings that are inserted into text, - // so strings in list will be encoded. wysiwyg, baby! - unfiltered.push(encodeURI(entryStr)); + // code hints show the unencoded string so the + // choices are easier to read. The encoded string + // will still be inserted into the editor. + unfiltered.push(entryStr); } }); @@ -225,7 +226,7 @@ define(function (require, exports, module) { }; /** - * Determines whether font hints are available in the current editor + * Determines whether url hints are available in the current editor * context. * * @param {Editor} editor @@ -361,7 +362,7 @@ define(function (require, exports, module) { }; /** - * Returns a list of availble font hints, if possible, for the current + * Returns a list of available url hints, if possible, for the current * editor context. * * @return {jQuery.Deferred|{ @@ -504,6 +505,10 @@ define(function (require, exports, module) { */ UrlCodeHints.prototype.insertHint = function (completion) { var mode = this.editor.getModeForSelection(); + + // Encode the string just prior to inserting the hint into the editor + completion = encodeURI(completion); + if (mode === "html") { return this.insertHtmlHint(completion); } else if (mode === "css") { @@ -754,4 +759,11 @@ define(function (require, exports, module) { // For unit testing exports.hintProvider = urlHints; }); + + $(ProjectManager).on("projectFilesChange", function (event, projectRoot) { + // Cache may or may not be stale. Main benefit of cache is to limit async lookups + // during typing. File tree updates cannot happen during typing, so it's probably + // not worth determining whether cache may still be valid. Just delete it. + exports.hintProvider.cachedHints = null; + }); }); diff --git a/src/extensions/default/WebPlatformDocs/WebPlatformDocs.less b/src/extensions/default/WebPlatformDocs/WebPlatformDocs.less index 29c6c67e9d9..b7ef4e1df93 100644 --- a/src/extensions/default/WebPlatformDocs/WebPlatformDocs.less +++ b/src/extensions/default/WebPlatformDocs/WebPlatformDocs.less @@ -62,7 +62,7 @@ float: left; padding-left: 20px; width: 35%; - word-break: break-all; + word-wrap: break-word; -webkit-hyphens: auto; hyphens: auto; diff --git a/src/filesystem/FileSystem.js b/src/filesystem/FileSystem.js index 76b4a371fb1..c2e9288d439 100644 --- a/src/filesystem/FileSystem.js +++ b/src/filesystem/FileSystem.js @@ -693,6 +693,7 @@ define(function (require, exports, module) { this._watchEntry(entry, watchedRoot, function (err) { if (err) { console.warn("Failed to watch root: ", entry.fullPath, err); + callback(err); return; } diff --git a/src/filesystem/FileSystemEntry.js b/src/filesystem/FileSystemEntry.js index 0c7864ddb9e..9a8a8ee6cf5 100644 --- a/src/filesystem/FileSystemEntry.js +++ b/src/filesystem/FileSystemEntry.js @@ -28,6 +28,11 @@ define(function (require, exports, module) { "use strict"; + var FileSystemError = require("filesystem/FileSystemError"); + + var VISIT_DEFAULT_MAX_DEPTH = 10, + VISIT_DEFAULT_MAX_ENTRIES = 30000; + /* Counter to give every entry a unique id */ var nextId = 0; @@ -231,39 +236,36 @@ define(function (require, exports, module) { }; /** - * Visit this entry and its descendents with the supplied visitor function. + * Private helper function for FileSystemEntry.visit that requires sanitized options. * + * @private * @param {function(FileSystemEntry): boolean} visitor - A visitor function, which is * applied to descendent FileSystemEntry objects. If the function returns false for * a particular Directory entry, that directory's descendents will not be visited. - * @param {{failFast: boolean=, maxDepth: number=}=} options - An optional set of options. + * @param {{failFast: boolean, maxDepth: number, maxEntriesCounter: {value: number}}} options * @param {function(?string)=} callback Callback with single "error" parameter. */ - FileSystemEntry.prototype.visit = function (visitor, options, callback) { - var DEFAULT_MAX_DEPTH = 100; + FileSystemEntry.prototype._visitHelper = function (visitor, options, callback) { + var failFast = options.failFast, + maxDepth = options.maxDepth, + maxEntriesCounter = options.maxEntriesCounter; - if (typeof options === "function") { - callback = options; - options = {}; - } else if (options === undefined) { - options = {}; + if (maxEntriesCounter.value-- <= 0 || maxDepth-- < 0) { + callback(failFast ? FileSystemError.TOO_MANY_ENTRIES : null); + return; } - callback = callback || function () {}; - - var maxDepth = typeof options.maxDepth === "number" ? options.maxDepth : DEFAULT_MAX_DEPTH, - continueTraversal = visitor(this) && maxDepth-- > 0; - - if (this.isFile || !continueTraversal) { + if (!visitor(this) || this.isFile) { callback(null); return; } this.getContents(function (err, entries) { var counter = entries ? entries.length : 0, - newOptions = { + nextOptions = { + failFast: failFast, maxDepth: maxDepth, - failFast: options.maxDepth + maxEntriesCounter: maxEntriesCounter }; if (err || counter === 0) { @@ -272,21 +274,69 @@ define(function (require, exports, module) { } entries.forEach(function (entry) { - entry.visit(visitor, newOptions, function (err) { - if (err && options.failFast) { + entry._visitHelper(visitor, nextOptions, function (err) { + if (err && failFast) { counter = 0; callback(err); return; } if (--counter === 0) { - callback(options.failFast ? err : null); + callback(failFast ? err : null); } }); }); }.bind(this)); }; + /** + * Visit this entry and its descendents with the supplied visitor function. + * + * @param {function(FileSystemEntry): boolean} visitor - A visitor function, which is + * applied to descendent FileSystemEntry objects. If the function returns false for + * a particular Directory entry, that directory's descendents will not be visited. + * @param {{failFast: boolean=, maxDepth: number=, maxEntries: number=}=} options - An optional set of options. + * @param {function(?string)=} callback Callback with single "error" parameter. + */ + FileSystemEntry.prototype.visit = function (visitor, options, callback) { + if (typeof options === "function") { + callback = options; + options = {}; + } else if (options === undefined) { + options = {}; + } + + if (options.failFast === undefined) { + options.failFast = false; + } + + if (options.maxDepth === undefined) { + options.maxDepth = VISIT_DEFAULT_MAX_DEPTH; + } + + if (options.maxEntries === undefined) { + options.maxEntries = VISIT_DEFAULT_MAX_ENTRIES; + } + + options.maxEntriesCounter = { value: options.maxEntries }; + + this._visitHelper(visitor, options, function (err) { + if (callback) { + if (err) { + callback(err); + return; + } + + if (options.maxEntriesCounter.value < 0) { + callback(FileSystemError.TOO_MANY_ENTRIES); + return; + } + + callback(null); + } + }); + }; + // Export this class module.exports = FileSystemEntry; }); diff --git a/src/filesystem/FileSystemError.js b/src/filesystem/FileSystemError.js index ed896ef5761..d54130b21b3 100644 --- a/src/filesystem/FileSystemError.js +++ b/src/filesystem/FileSystemError.js @@ -41,6 +41,7 @@ define(function (require, exports, module) { NOT_READABLE : "NotReadable", NOT_WRITABLE : "NotWritable", OUT_OF_SPACE : "OutOfSpace", + TOO_MANY_ENTRIES : "TooManyEntries", ALREADY_EXISTS : "AlreadyExists" // FUTURE: Add remote connection errors: timeout, not logged in, connection err, etc. }; diff --git a/src/htmlContent/contributors-list.html b/src/htmlContent/contributors-list.html index ae5d1eb7ab0..50559e72cfc 100644 --- a/src/htmlContent/contributors-list.html +++ b/src/htmlContent/contributors-list.html @@ -1,5 +1 @@ -{{#.}} - - {{login}} - -{{/.}} \ No newline at end of file +{{#.}}{{login}}{{/.}} diff --git a/src/language/languages.json b/src/language/languages.json index b242c8d8038..c0c0253d40f 100644 --- a/src/language/languages.json +++ b/src/language/languages.json @@ -21,7 +21,7 @@ "css": { "name": "CSS", "mode": "css", - "fileExtensions": ["css"], + "fileExtensions": ["css", "css.erb"], "blockComment": ["/*", "*/"] }, @@ -43,17 +43,10 @@ "ejs": { "name": "EJS", "mode": ["htmlembedded", "application/x-ejs"], - "fileExtensions": ["ejs"], + "fileExtensions": ["ejs", "dust"], "blockComment": [""] }, - "dust": { - "name": "Dust.js", - "mode": ["htmlembedded", "application/x-ejs"], - "fileExtensions": ["dust"], - "blockComment": [""] - }, - "erb_html": { "name": "Embedded Ruby", "mode": ["htmlembedded", "application/x-erb"], @@ -122,7 +115,7 @@ "csharp": { "name": "C#", "mode": ["clike", "text/x-csharp"], - "fileExtensions": ["cs", "cshtml", "asax", "ashx"], + "fileExtensions": ["cs", "asax", "ashx"], "blockComment": ["/*", "*/"], "lineComment": ["//"] }, diff --git a/src/nls/fa-ir/strings.js b/src/nls/fa-ir/strings.js index c7bd7ef95f7..860688c8861 100644 --- a/src/nls/fa-ir/strings.js +++ b/src/nls/fa-ir/strings.js @@ -200,7 +200,6 @@ define({ "CMD_FILE_SAVE_ALL" : "ذخیره همه", "CMD_FILE_SAVE_AS" : "ذخیره همه\u2026", "CMD_LIVE_FILE_PREVIEW" : "پیش نمایش زنده", - "CMD_LIVE_HIGHLIGHT" : "نشانه گذاری پیش نمایش", "CMD_PROJECT_SETTINGS" : "تنظیمات پروژه\u2026", "CMD_FILE_RENAME" : "تغییر نام", "CMD_FILE_DELETE" : "حذف", @@ -226,9 +225,9 @@ define({ "CMD_FIND_NEXT" : "بعدی", "CMD_FIND_PREVIOUS" : "قبلی", "CMD_REPLACE" : "جستجو و جایگزینی", - "CMD_INDENT" : "برجسته سازی", - "CMD_UNINDENT" : "حذف برجستگی", - "CMD_DUPLICATE" : "دو نسخه کردن", + "CMD_INDENT" : "یک فاصله از چپ", + "CMD_UNINDENT" : "حذف یک فاصله از جپ", + "CMD_DUPLICATE" : "دونسخه کردن خط", "CMD_DELETE_LINES" : "حذف خط", "CMD_COMMENT" : "تعویض خط به نظر", "CMD_BLOCK_COMMENT" : "تعویض نظر به خط", @@ -251,6 +250,8 @@ define({ "CMD_TOGGLE_LINE_NUMBERS" : "شماره گذاری خط ها", "CMD_TOGGLE_ACTIVE_LINE" : "نشانه گذاری خط فعال", "CMD_TOGGLE_WORD_WRAP" : "شکستن عبارات طولانی", + "CMD_LIVE_HIGHLIGHT" : "نشانه گذاری پیش نمایش", + "CMD_VIEW_TOGGLE_INSPECTION" : "نشانه گذاری فایل های تغییر یافته برای ذخیره سازی", "CMD_SORT_WORKINGSET_BY_ADDED" : "مرتب سازی بر اساس ترتیب افزودن", "CMD_SORT_WORKINGSET_BY_NAME" : "مرتب سازی بر اساس نام", "CMD_SORT_WORKINGSET_BY_TYPE" : "مرتب سازی بر اساس نوع", @@ -409,7 +410,7 @@ define({ "CMD_REFRESH_WINDOW" : "بارگذاری مجدد براکتس", "CMD_NEW_BRACKETS_WINDOW" : "یک پنجره جدید از براکتس باز کنید", "CMD_SWITCH_LANGUAGE" : "انتخاب زبان", - "CMD_RUN_UNIT_TESTS" : "اجرای test", + "CMD_RUN_UNIT_TESTS" : "برسی برای اجرا", "CMD_SHOW_PERF_DATA" : "نمایش داده های عملکردی", "CMD_ENABLE_NODE_DEBUGGER" : "فعال سازی اشکال زدای گرهی", "CMD_LOG_NODE_STATE" : "ورود گره به حالت کنسول", @@ -433,7 +434,7 @@ define({ "LOCALE_IT" : "Italian", "LOCALE_JA" : "Japanese", "LOCALE_NB" : "Norwegian", - "LOCALE_fa_ir" : "Persian-پارسی", + "LOCALE_FA_IR" : "Persian-پارسی", "LOCALE_PL" : "Polish", "LOCALE_PT_BR" : "Portuguese, Brazil", "LOCALE_PT_PT" : "Portuguese", diff --git a/src/nls/fr/strings.js b/src/nls/fr/strings.js index 6b2daa83ba5..533686dd894 100644 --- a/src/nls/fr/strings.js +++ b/src/nls/fr/strings.js @@ -455,6 +455,7 @@ define({ "LOCALE_IT": "Italien", "LOCALE_JA": "Japonais", "LOCALE_NB": "Norvégien", + "LOCALE_FA_IR": "Persan/Farsi", "LOCALE_PL": "Polonais", "LOCALE_PT_BR": "Portugais (Brésil)", "LOCALE_PT_PT": "Portugais", diff --git a/src/nls/ja/strings.js b/src/nls/ja/strings.js index df32ce8f0f8..905fc35b219 100644 --- a/src/nls/ja/strings.js +++ b/src/nls/ja/strings.js @@ -455,6 +455,7 @@ define({ "LOCALE_IT": "イタリア語", "LOCALE_JA": "日本語", "LOCALE_NB": "ノルウェー語", + "LOCALE_FA_IR": "ペルシャ語-ファルシ語", "LOCALE_PL": "ポーランド語", "LOCALE_PT_BR": "ポルトガル語 (ブラジル)", "LOCALE_PT_PT": "ポルトガル語", diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index 62b78fd74d7..f86cf620aa6 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -67,6 +67,10 @@ define({ // Application error strings "ERROR_IN_BROWSER_TITLE" : "Oops! {APP_NAME} doesn't run in browsers yet.", "ERROR_IN_BROWSER" : "{APP_NAME} is built in HTML, but right now it runs as a desktop app so you can use it to edit local files. Please use the application shell in the github.com/adobe/brackets-shell repo to run {APP_NAME}.", + + // ProjectManager max files error string + "ERROR_MAX_FILES_TITLE" : "Error Indexing Files", + "ERROR_MAX_FILES" : "The maximum number of files have been indexed. Actions that look up files in the index may function incorrectly.", // Live Development error strings "ERROR_LAUNCHING_BROWSER_TITLE" : "Error launching browser", @@ -451,7 +455,7 @@ define({ "LOCALE_IT" : "Italian", "LOCALE_JA" : "Japanese", "LOCALE_NB" : "Norwegian", - "LOCALE_FA_IR" : "Persian", + "LOCALE_FA_IR" : "Persian-Farsi", "LOCALE_PL" : "Polish", "LOCALE_PT_BR" : "Portuguese, Brazil", "LOCALE_PT_PT" : "Portuguese", diff --git a/src/project/ProjectManager.js b/src/project/ProjectManager.js index 6ac0db31352..12d70a98ce1 100644 --- a/src/project/ProjectManager.js +++ b/src/project/ProjectManager.js @@ -876,12 +876,26 @@ define(function (require, exports, module) { return updateWelcomeProjectPath(_prefs.getValue("projectPath")); } + /** + * Error dialog when max files in index is hit + * @return {Dialog} + */ + function _showMaxFilesDialog() { + return Dialogs.showModalDialog( + DefaultDialogs.DIALOG_ID_ERROR, + Strings.ERROR_MAX_FILES_TITLE, + Strings.ERROR_MAX_FILES + ); + } + function _watchProjectRoot(rootPath) { FileSystem.on("change", _fileSystemChange); FileSystem.on("rename", _fileSystemRename); FileSystem.watch(FileSystem.getDirectoryForPath(rootPath), _shouldShowName, function (err) { - if (err) { + if (err === FileSystemError.TOO_MANY_ENTRIES) { + _showMaxFilesDialog(); + } else if (err) { console.error("Error watching project root: ", rootPath, err); } }); diff --git a/src/search/FindInFiles.js b/src/search/FindInFiles.js index 2e8811d2a71..e2eebf4275e 100644 --- a/src/search/FindInFiles.js +++ b/src/search/FindInFiles.js @@ -817,6 +817,13 @@ define(function (require, exports, module) { var currentEditor = EditorManager.getActiveEditor(), initialString = currentEditor && currentEditor.getSelectedText(); + if (dialog && !dialog.closed && dialog.hasOwnProperty("modalBar") && dialog.modalBar) { + // The modalBar was already up. When creating the new modalBar, copy the + // current query instead of using the passed-in selected text. + initialString = dialog.getDialogTextField().val(); + dialog.modalBar.close(true, false); + } + dialog = new FindInFilesDialog(); searchResults = {}; currentStart = 0; diff --git a/src/styles/brackets.less b/src/styles/brackets.less index 2699b52c98e..7423d536f5e 100644 --- a/src/styles/brackets.less +++ b/src/styles/brackets.less @@ -297,9 +297,10 @@ a, img { left: 5px; width: 40px; text-align: center; - background-color: rgba(0,0,0,0.8); - color: white; - border-radius: 4px; + background-color: rgba(0, 0, 0, 0.8); + color: #fff; + border-radius: 3px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); } } diff --git a/src/styles/brackets_theme_default.less b/src/styles/brackets_theme_default.less index d69ef05e4bf..590c2ff9685 100644 --- a/src/styles/brackets_theme_default.less +++ b/src/styles/brackets_theme_default.less @@ -115,7 +115,7 @@ /* selection colors */ @selection-color-focused: #abdffa; -@selection-color-unfocused: #e9e9e9; +@selection-color-unfocused: #d5dee3; /* background color of the line that has the cursor */ @activeline-bgcolor: #ebefef; diff --git a/test/spec/FileSystem-test.js b/test/spec/FileSystem-test.js index 59c9fbe70dd..daa909ff3dc 100644 --- a/test/spec/FileSystem-test.js +++ b/test/spec/FileSystem-test.js @@ -502,6 +502,154 @@ define(function (require, exports, module) { }); }); + describe("FileSystemEntry.visit", function () { + function getContentsCallback() { + var callback = function (err, contents) { + callback.error = err; + callback.contents = contents; + callback.wasCalled = true; + }; + return callback; + } + + beforeEach(function () { + function initEntry(entry, command, args) { + var cb = getContentsCallback(); + + args.push(cb); + runs(function () { + entry[command].apply(entry, args); + }); + waitsFor(function () { return cb.wasCalled; }); + runs(function () { + expect(cb.error).toBeFalsy(); + }); + } + + function initDir(path) { + initEntry(fileSystem.getDirectoryForPath(path), "create", []); + } + + function initFile(path) { + initEntry(fileSystem.getFileForPath(path), "write", ["abc"]); + } + + initDir("/visit/"); + initFile("/visit/file.txt"); + initDir("/visit/subdir1/"); + initDir("/visit/subdir2/"); + initFile("/visit/subdir1/subfile11.txt"); + initFile("/visit/subdir1/subfile12.txt"); + initFile("/visit/subdir2/subfile21.txt"); + initFile("/visit/subdir2/subfile22.txt"); + }); + + it("should visit all entries by default", function () { + var directory = fileSystem.getDirectoryForPath("/visit/"), + results = {}, + visitor = function (entry) { + results[entry.fullPath] = entry; + return true; + }; + + var cb = getContentsCallback(); + runs(function () { + directory.visit(visitor, cb); + }); + waitsFor(function () { return cb.wasCalled; }); + runs(function () { + expect(cb.error).toBeFalsy(); + expect(Object.keys(results).length).toBe(8); + expect(results["/visit/"]).toBeTruthy(); + expect(results["/visit/file.txt"]).toBeTruthy(); + expect(results["/visit/subdir1/"]).toBeTruthy(); + expect(results["/visit/subdir2/"]).toBeTruthy(); + expect(results["/visit/subdir1/subfile11.txt"]).toBeTruthy(); + expect(results["/visit/subdir1/subfile12.txt"]).toBeTruthy(); + expect(results["/visit/subdir2/subfile21.txt"]).toBeTruthy(); + expect(results["/visit/subdir2/subfile21.txt"]).toBeTruthy(); + expect(results["/"]).not.toBeTruthy(); + }); + }); + + it("should visit with a specified maximum depth", function () { + var directory = fileSystem.getDirectoryForPath("/visit/"), + results = {}, + visitor = function (entry) { + results[entry.fullPath] = entry; + return true; + }; + + var cb = getContentsCallback(); + runs(function () { + directory.visit(visitor, {maxDepth: 1}, cb); + }); + waitsFor(function () { return cb.wasCalled; }); + runs(function () { + expect(cb.error).toBeFalsy(); + expect(Object.keys(results).length).toBe(4); + expect(results["/visit/"]).toBeTruthy(); + expect(results["/visit/file.txt"]).toBeTruthy(); + expect(results["/visit/subdir1/"]).toBeTruthy(); + expect(results["/visit/subdir2/"]).toBeTruthy(); + expect(results["/visit/subdir1/subfile11.txt"]).not.toBeTruthy(); + expect(results["/visit/subdir1/subfile12.txt"]).not.toBeTruthy(); + expect(results["/visit/subdir2/subfile21.txt"]).not.toBeTruthy(); + expect(results["/visit/subdir2/subfile21.txt"]).not.toBeTruthy(); + expect(results["/"]).not.toBeTruthy(); + }); + }); + + it("should visit with a specified maximum number of entries", function () { + var directory = fileSystem.getDirectoryForPath("/visit/"), + results = {}, + visitor = function (entry) { + results[entry.fullPath] = entry; + return true; + }; + + var cb = getContentsCallback(); + runs(function () { + directory.visit(visitor, {maxEntries: 4}, cb); + }); + waitsFor(function () { return cb.wasCalled; }); + runs(function () { + expect(cb.error).toBe(FileSystemError.TOO_MANY_ENTRIES); + expect(Object.keys(results).length).toBe(4); + expect(results["/visit/"]).toBeTruthy(); + expect(results["/visit/file.txt"]).toBeTruthy(); + expect(results["/"]).not.toBeTruthy(); + }); + }); + + it("should visit only children of directories admitted by the filter", function () { + var directory = fileSystem.getDirectoryForPath("/visit/"), + results = {}, + visitor = function (entry) { + results[entry.fullPath] = entry; + return entry.name === "visit" || /1$/.test(entry.name); + }; + + var cb = getContentsCallback(); + runs(function () { + directory.visit(visitor, cb); + }); + waitsFor(function () { return cb.wasCalled; }); + runs(function () { + expect(cb.error).toBeFalsy(); + expect(Object.keys(results).length).toBe(6); + expect(results["/visit/"]).toBeTruthy(); + expect(results["/visit/file.txt"]).toBeTruthy(); + expect(results["/visit/subdir1/"]).toBeTruthy(); + expect(results["/visit/subdir2/"]).toBeTruthy(); + expect(results["/visit/subdir1/subfile11.txt"]).toBeTruthy(); + expect(results["/visit/subdir1/subfile12.txt"]).toBeTruthy(); + expect(results["/visit/subdir2/subfile21.txt"]).not.toBeTruthy(); + expect(results["/visit/subdir2/subfile21.txt"]).not.toBeTruthy(); + expect(results["/"]).not.toBeTruthy(); + }); + }); + }); describe("Event timing", function () { diff --git a/test/spec/LanguageManager-test.js b/test/spec/LanguageManager-test.js index 9bde071a5cd..4b484b66e04 100644 --- a/test/spec/LanguageManager-test.js +++ b/test/spec/LanguageManager-test.js @@ -36,7 +36,6 @@ define(function (require, exports, module) { FileSystem = require("filesystem/FileSystem"); describe("LanguageManager", function () { - this.category = "integration"; beforeEach(function () { waitsForDone(LanguageManager.ready, "LanguageManager ready", 10000); @@ -403,6 +402,7 @@ define(function (require, exports, module) { }); describe("rename file extension", function () { + this.category = "integration"; it("should update the document's language when a file is renamed", function () { var tempDir = SpecRunnerUtils.getTempDirectory(), @@ -475,7 +475,6 @@ define(function (require, exports, module) { }); waitsForDone(renameDeferred.promise(), "old file rename"); - var unlinkDeferred = $.Deferred(); runs(function () { html = LanguageManager.getLanguage("html"); @@ -490,16 +489,7 @@ define(function (require, exports, module) { // cleanup doc.releaseRef(); - - oldFile.unlink(function (err) { - if (err) { - unlinkDeferred.reject(err); - } else { - unlinkDeferred.resolve(); - } - }); }); - waitsForDone(unlinkDeferred.promise(), "new file unlink"); SpecRunnerUtils.closeTestWindow(); diff --git a/test/spec/MockFileSystemImpl.js b/test/spec/MockFileSystemImpl.js index 8549142604a..69070e25029 100644 --- a/test/spec/MockFileSystemImpl.js +++ b/test/spec/MockFileSystemImpl.js @@ -272,18 +272,21 @@ define(function (require, exports, module) { options = null; } - var cb = _getCallback("writeFile", path, callback), - notify = _getNotification("writeFile", path, _sendWatcherNotification); - - if (!_data[path]) { - _data[path] = { - isFile: true - }; - } - _data[path].contents = data; - _data[path].mtime = Date.now(); - cb(null); - notify(path); + exists(path, function (exists) { + var cb = _getCallback("writeFile", path, callback), + notification = exists ? _sendWatcherNotification : _sendDirectoryWatcherNotification, + notify = _getNotification("writeFile", path, notification); + + if (!_data[path]) { + _data[path] = { + isFile: true + }; + } + _data[path].contents = data; + _data[path].mtime = Date.now(); + cb(null); + notify(path); + }); } function unlink(path, callback) {