diff --git a/package.json b/package.json index 6c2f371db21..379389bfa84 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "Brackets", - "version": "0.33.0-0", - "apiVersion": "0.33.0", + "version": "0.34.0-0", + "apiVersion": "0.34.0", "homepage": "http://brackets.io", "issues": { "url": "http://github.com/adobe/brackets/issues" diff --git a/src/LiveDevelopment/LiveDevelopment.js b/src/LiveDevelopment/LiveDevelopment.js index 0c7dd96b5ab..ab55f1a236f 100644 --- a/src/LiveDevelopment/LiveDevelopment.js +++ b/src/LiveDevelopment/LiveDevelopment.js @@ -569,7 +569,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, @@ -799,8 +799,19 @@ define(function LiveDevelopment(require, exports, module) { * the status accordingly. */ function cleanup() { - _setStatus(STATUS_INACTIVE, reason || "explicit_close"); - deferred.resolve(); + // Need to do this in order to trigger the corresponding CloseLiveBrowser cleanups required on + // the native Mac side + var closeDeferred = (brackets.platform === "mac") ? NativeApp.closeLiveBrowser() : $.Deferred().resolve(); + closeDeferred.done(function () { + _setStatus(STATUS_INACTIVE, reason || "explicit_close"); + deferred.resolve(); + }).fail(function (err) { + if (err) { + reason += " (" + err + ")"; + } + _setStatus(STATUS_INACTIVE, reason || "explicit_close"); + deferred.resolve(); + }); } if (_openDeferred) { @@ -1034,7 +1045,7 @@ define(function LiveDevelopment(require, exports, module) { if (id === Dialogs.DIALOG_BTN_OK) { // User has chosen to reload Chrome, quit the running instance _setStatus(STATUS_INACTIVE); - NativeApp.closeLiveBrowser() + _close() .done(function () { browserStarted = false; window.setTimeout(function () { @@ -1049,7 +1060,17 @@ define(function LiveDevelopment(require, exports, module) { _openDeferred.reject("CLOSE_LIVE_BROWSER"); }); } else { - _openDeferred.reject("CANCEL"); + _close() + .done(function () { + browserStarted = false; + _openDeferred.reject("CANCEL"); + }) + .fail(function (err) { + // Report error? + _setStatus(STATUS_ERROR); + browserStarted = false; + _openDeferred.reject("CLOSE_LIVE_BROWSER"); + }); } }); diff --git a/src/config.json b/src/config.json index 0cd61930a5e..974cc8c321a 100644 --- a/src/config.json +++ b/src/config.json @@ -18,8 +18,8 @@ "linting.enabled_by_default": true }, "name": "Brackets", - "version": "0.33.0-0", - "apiVersion": "0.33.0", + "version": "0.34.0-0", + "apiVersion": "0.34.0", "homepage": "http://brackets.io", "issues": { "url": "http://github.com/adobe/brackets/issues" diff --git a/src/document/DocumentCommandHandlers.js b/src/document/DocumentCommandHandlers.js index 622d67d8ae7..b35b10ee63d 100644 --- a/src/document/DocumentCommandHandlers.js +++ b/src/document/DocumentCommandHandlers.js @@ -276,7 +276,7 @@ define(function (require, exports, module) { if (doc) { DocumentManager.addToWorkingSet(doc.file); } - _defaultOpenDialogFullPath = FileUtils.getDirectoryPath(EditorManager.getCurrentlyViewedPath); + _defaultOpenDialogFullPath = FileUtils.getDirectoryPath(EditorManager.getCurrentlyViewedPath()); }) // Send the resulting document that was opened .then(result.resolve, result.reject); 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 258c1aca8d3..850bd870bce 100644 --- a/src/editor/ImageViewer.js +++ b/src/editor/ImageViewer.js @@ -98,7 +98,7 @@ 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 fileEntry = new NativeFileSystem.FileEntry(fullPath); fileEntry.getMetadata( @@ -110,7 +110,7 @@ define(function (require, exports, module) { $("#img-data").html(dimensionString + sizeString); }, function (error) { - $("#img-data").text(dimensionString); + $("#img-data").html(dimensionString); } ); $("#image-holder").show(); diff --git a/src/extensibility/ExtensionManagerDialog.js b/src/extensibility/ExtensionManagerDialog.js index f94d2de8b49..a37efe529f3 100644 --- a/src/extensibility/ExtensionManagerDialog.js +++ b/src/extensibility/ExtensionManagerDialog.js @@ -148,13 +148,15 @@ define(function (require, exports, module) { $search, $searchClear, context = { Strings: Strings, showRegistry: !!brackets.config.extension_registry }, - models = [new ExtensionManagerViewModel.InstalledViewModel()]; + models = []; // Load registry only if the registry URL exists if (context.showRegistry) { models.push(new ExtensionManagerViewModel.RegistryViewModel()); } + models.push(new ExtensionManagerViewModel.InstalledViewModel()); + function updateSearchDisabled() { var model = models[_activeTabIndex], searchDisabled = ($search.val() === "") && @@ -189,13 +191,36 @@ define(function (require, exports, module) { $(this).tab("show"); }); + // Update & hide/show the notification overlay on a tab's icon, based on its model's notifyCount + function updateNotificationIcon(index) { + var model = models[index], + $notificationIcon = $dlg.find(".nav-tabs li").eq(index).find(".notification"); + if (model.notifyCount) { + $notificationIcon.text(model.notifyCount); + $notificationIcon.show(); + } else { + $notificationIcon.hide(); + } + } + // Initialize models and create a view for each model var modelInitPromise = Async.doInParallel(models, function (model, index) { var view = new ExtensionManagerView(), - promise = view.initialize(model); + promise = view.initialize(model), + lastNotifyCount; promise.always(function () { views[index] = view; + + lastNotifyCount = model.notifyCount; + updateNotificationIcon(index); + }); + + $(model).on("change", function () { + if (lastNotifyCount !== model.notifyCount) { + lastNotifyCount = model.notifyCount; + updateNotificationIcon(index); + } }); return promise; diff --git a/src/extensibility/ExtensionManagerViewModel.js b/src/extensibility/ExtensionManagerViewModel.js index 843af97af15..1cba64368bc 100644 --- a/src/extensibility/ExtensionManagerViewModel.js +++ b/src/extensibility/ExtensionManagerViewModel.js @@ -117,6 +117,12 @@ define(function (require, exports, module) { */ ExtensionManagerViewModel.prototype.message = null; + /** + * @type {number} + * Number to show in tab's notification icon. No icon shown if 0. + */ + ExtensionManagerViewModel.prototype.notifyCount = 0; + /** * @private {$.Promise} * Internal use only to track when initialization fails, see usage in _updateMessage. @@ -374,6 +380,8 @@ define(function (require, exports, module) { }); this._sortFullSet(); this._setInitialFilter(); + this._countUpdates(); + return new $.Deferred().resolve().promise(); }; @@ -399,6 +407,20 @@ define(function (require, exports, module) { }); }; + /** + * @private + * Updates notifyCount based on number of extensions with an update available + */ + InstalledViewModel.prototype._countUpdates = function () { + var self = this; + this.notifyCount = 0; + this.sortedFullSet.forEach(function (key) { + if (self.extensions[key].installInfo.updateAvailable) { + self.notifyCount++; + } + }); + }; + /** * @private * Updates the initial set and filter as necessary when the status of an extension changes, @@ -412,6 +434,7 @@ define(function (require, exports, module) { if (index !== -1 && !this.extensions[id].installInfo) { // This was in our set, but was uninstalled. Remove it. this.sortedFullSet.splice(index, 1); + this._countUpdates(); // may also affect update count refilter = true; } else if (index === -1 && this.extensions[id].installInfo) { // This was not in our set, but is now installed. Add it and resort. @@ -422,6 +445,12 @@ define(function (require, exports, module) { if (refilter) { this.filter(this._lastQuery || "", true); } + + if (this.extensions[id].installInfo) { + // If our count of available updates may have been affected, re-count + this._countUpdates(); + } + ExtensionManagerViewModel.prototype._handleStatusChange.call(this, e, id); }; diff --git a/src/extensions/default/CSSCodeHints/CSSProperties.json b/src/extensions/default/CSSCodeHints/CSSProperties.json index 889173f53bf..888720037ae 100644 --- a/src/extensions/default/CSSCodeHints/CSSProperties.json +++ b/src/extensions/default/CSSCodeHints/CSSProperties.json @@ -79,7 +79,7 @@ "counter-reset": {"values": ["none", "inherit"]}, "cursor": {"values": ["auto", "crosshair", "e-resize", "default", "help", "move", "n-resize", "ne-resize", "nw-resize", "pointer", "progress", "s-resize", "se-resize", "sw-resize", "text", "w-resize", "wait", "inherit"]}, "direction": {"values": ["ltr", "rtl", "inherit"]}, - "display": {"values": ["block", "flex", "inline", "inline-block", "inline-flex", "inline-table", "list-item", "none", "table", "table-caption", "table-cell", "table-column", "table-column-group", "table-footer-group", "table-header-group", "table-row", "table-row-group", "inherit"]}, + "display": {"values": ["block", "flex", "grid", "inline", "inline-block", "inline-flex", "inline-grid", "inline-table", "list-item", "none", "run-in", "table", "table-caption", "table-cell", "table-column", "table-column-group", "table-footer-group", "table-header-group", "table-row", "table-row-group", "inherit"]}, "empty-cells": {"values": ["hide", "show", "inherit"]}, "flex": {"values": ["none"]}, "flex-basis": {"values": []}, @@ -182,7 +182,7 @@ "text-transform": {"values": ["capitalize", "full-width", "lowercase", "none", "uppercase", "inherit"]}, "text-underline-position": {"values": ["alphabetic", "auto", "below", "left", "right"]}, "top": {"values": ["auto", "inherit"]}, - "transform": {"values": ["matrix()", "matrix3d()", "perspective()", "rotate()", "rotate3d()", "rotateX()", "rotateY()", "rotateZ()", "scale()", "scale3d()", "scaleX()", "scaleY()", "scaleZ()", "skewX()", "skewY()", "translate()", "translate3d()", "translateX()", "translateY()", "translateZ()"]}, + "transform": {"values": ["matrix()", "matrix3d()", "none", "perspective()", "rotate()", "rotate3d()", "rotateX()", "rotateY()", "rotateZ()", "scale()", "scale3d()", "scaleX()", "scaleY()", "scaleZ()", "skewX()", "skewY()", "translate()", "translate3d()", "translateX()", "translateY()", "translateZ()"]}, "transform-origin": {"values": ["bottom", "center", "left", "right", "top"]}, "transform-style": {"values": ["flat", "preserve-3d"]}, "transition": {"values": []}, diff --git a/src/extensions/default/JavaScriptCodeHints/ScopeManager.js b/src/extensions/default/JavaScriptCodeHints/ScopeManager.js index c3b7851da65..32a2bc292e2 100644 --- a/src/extensions/default/JavaScriptCodeHints/ScopeManager.js +++ b/src/extensions/default/JavaScriptCodeHints/ScopeManager.js @@ -60,7 +60,7 @@ define(function (require, exports, module) { var MAX_HINTS = 30, // how often to reset the tern server LARGE_LINE_CHANGE = 100, - LARGE_LINE_COUNT = 250, + LARGE_LINE_COUNT = 2000, OFFSET_ZERO = {line: 0, ch: 0}; /** diff --git a/src/extensions/default/JavaScriptCodeHints/unittests.js b/src/extensions/default/JavaScriptCodeHints/unittests.js index 1fc1a2bb9e4..045a69104b6 100644 --- a/src/extensions/default/JavaScriptCodeHints/unittests.js +++ b/src/extensions/default/JavaScriptCodeHints/unittests.js @@ -894,7 +894,7 @@ define(function (require, exports, module) { }); }); - it("should list function defined from .prototype", function () { + xit("should list function defined from .prototype", function () { var start = { line: 59, ch: 5 }; testEditor.setCursorPos(start); @@ -905,7 +905,7 @@ define(function (require, exports, module) { }); - it("should list function type defined from .prototype", function () { + xit("should list function type defined from .prototype", function () { var start = { line: 59, ch: 10 }; testEditor.setCursorPos(start); runs(function () { @@ -913,7 +913,7 @@ define(function (require, exports, module) { }); }); - it("should list function inhertated from super class", function () { + xit("should list function inherited from super class", function () { var start = { line: 79, ch: 11 }; testEditor.setCursorPos(start); var hintObj = expectHints(JSCodeHints.jsHintProvider); 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 c5d9f58599f..279c839d6fa 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/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/htmlContent/extension-manager-dialog.html b/src/htmlContent/extension-manager-dialog.html index f363761798d..ae730ba6ef8 100644 --- a/src/htmlContent/extension-manager-dialog.html +++ b/src/htmlContent/extension-manager-dialog.html @@ -1,11 +1,11 @@