diff --git a/src/editor/CodeHintList.js b/src/editor/CodeHintList.js index 964e1ed447c..5a132281d75 100644 --- a/src/editor/CodeHintList.js +++ b/src/editor/CodeHintList.js @@ -152,7 +152,7 @@ define(function (require, exports, module) { ViewUtils.scrollElementIntoView($view, $item, false); if (this.handleHighlight) { - this.handleHighlight($item.find("a")); + this.handleHighlight($item.find("a"), this.$hintMenu.find("#codehint-desc")); } } }; @@ -191,6 +191,7 @@ define(function (require, exports, module) { this.hints = hintObj.hints; this.hints.handleWideResults = hintObj.handleWideResults; + this.enableDescription = hintObj.enableDescription; // if there is no match, assume name is already a formatted jQuery // object; otherwise, use match to format name for display. @@ -265,6 +266,17 @@ define(function (require, exports, module) { // attach to DOM $parent.append($ul); + $parent.find(".hint-list-offset").remove(); + $("
").appendTo($parent); + + // If a a description field requested attach one + if (this.enableDescription) { + // Remove the desc element first to ensure DOM order + $parent.find("#codehint-desc").remove(); + $parent.append(""); + $ul.addClass("withDesc"); + $parent.find(".hint-list-offset").addClass("withDesc"); + } this._setSelectedIndex(selectInitial ? 0 : -1); } }; @@ -283,7 +295,9 @@ define(function (require, exports, module) { textHeight = this.editor.getTextHeight(), $window = $(window), $menuWindow = this.$hintMenu.children("ul"), - menuHeight = $menuWindow.outerHeight(); + $descElement = this.$hintMenu.find("#codehint-desc"), + descOverhang = $descElement.length === 1 ? $descElement.height() : 0, + menuHeight = $menuWindow.outerHeight() + descOverhang; // TODO Ty: factor out menu repositioning logic so code hints and Context menus share code // adjust positioning so menu is not clipped off bottom or right @@ -304,6 +318,13 @@ define(function (require, exports, module) { availableWidth = menuWidth + Math.abs(rightOverhang); } + //Creating the offset element for hint description element + var descOffset = this.$hintMenu.find("ul.dropdown-menu")[0].getBoundingClientRect().height; + if (descOffset === 0) { + descOffset = menuHeight - descOverhang; + } + this.$hintMenu.find(".hint-list-offset").css("height", descOffset - 1); + return {left: posLeft, top: posTop, width: availableWidth}; }; diff --git a/src/editor/CodeHintManager.js b/src/editor/CodeHintManager.js index 8a2cdf28e69..925ffcb61ec 100644 --- a/src/editor/CodeHintManager.js +++ b/src/editor/CodeHintManager.js @@ -507,10 +507,21 @@ define(function (require, exports, module) { sessionEditor = editor; hintList = new CodeHintList(sessionEditor, insertHintOnTab, maxCodeHints); - hintList.onHighlight(function ($hint) { - // If the current hint provider listening for hint item highlight change - if (sessionProvider.onHighlight) { - sessionProvider.onHighlight($hint); + hintList.onHighlight(function ($hint, $hintDescContainer) { + if (hintList.enableDescription && $hintDescContainer && $hintDescContainer.length) { + // If the current hint provider listening for hint item highlight change + if (sessionProvider.onHighlight) { + sessionProvider.onHighlight($hint, $hintDescContainer); + } + + // Update the hint description + if (sessionProvider.updateHintDescription) { + sessionProvider.updateHintDescription($hint, $hintDescContainer); + } + } else { + if (sessionProvider.onHighlight) { + sessionProvider.onHighlight($hint); + } } }); hintList.onSelect(function (hint) { diff --git a/src/extensions/default/PhpTooling/CodeHintsProvider.js b/src/extensions/default/PhpTooling/CodeHintsProvider.js index 395c9c599f2..3f54d82f2e1 100644 --- a/src/extensions/default/PhpTooling/CodeHintsProvider.js +++ b/src/extensions/default/PhpTooling/CodeHintsProvider.js @@ -36,30 +36,27 @@ define(function (require, exports, module) { preferPrefixMatches: true }); - var phpSuperGlobalVariables = JSON.parse(require("text!phpGlobals.json")); + var phpSuperGlobalVariables = JSON.parse(require("text!phpGlobals.json")), + hintType = { + "2": "Method", + "3": "Function", + "4": "Constructor", + "6": "Variable", + "7": "Class", + "8": "Interface", + "9": "Module", + "10": "Property", + "14": "Keyword", + "21": "Constant" + }; function CodeHintsProvider(client) { this.defaultCodeHintProviders = new DefaultProviders.CodeHintsProvider(client); } - function formatTypeDataForToken($hintObj, token) { + function setStyleAndCacheToken($hintObj, token) { $hintObj.addClass('brackets-hints-with-type-details'); - if (token.detail) { - if (token.detail.trim() !== '?') { - if (token.detail.length < 30) { - $('' + token.detail.split('->').join(':').toString().trim() + '').appendTo($hintObj).addClass("brackets-hints-type-details"); - } - $('' + token.detail.split('->').join(':').toString().trim() + '').appendTo($hintObj).addClass("hint-description"); - } - } else { - if (token.keyword) { - $('keyword').appendTo($hintObj).addClass("brackets-hints-keyword"); - } - } - if (token.documentation) { - $hintObj.attr('title', token.documentation); - $('').text(token.documentation.trim()).appendTo($hintObj).addClass("hint-doc"); - } + $hintObj.data('completionItem', token); } function filterWithQueryAndMatcher(hints, query) { @@ -146,7 +143,7 @@ define(function (require, exports, module) { } $fHint.data("token", element); - formatTypeDataForToken($fHint, element); + setStyleAndCacheToken($fHint, element); hints.push($fHint); }); } @@ -154,6 +151,7 @@ define(function (require, exports, module) { var token = self.query; $deferredHints.resolve({ "hints": hints, + "enableDescription": true, "selectInitial": token && /\S/.test(token) && isNaN(parseInt(token, 10)) // If the active token is blank then don't put default selection }); }).fail(function () { @@ -167,5 +165,33 @@ define(function (require, exports, module) { return this.defaultCodeHintProviders.insertHint($hint); }; + CodeHintsProvider.prototype.updateHintDescription = function ($hint, $hintDescContainer) { + var $hintObj = $hint.find('.brackets-hints-with-type-details'), + token = $hintObj.data('completionItem'), + $desc = $('
'); + + if(!token) { + $hintDescContainer.empty(); + return; + } + + if (token.detail) { + if (token.detail.trim() !== '?') { + $('
' + token.detail.split('->').join(':').toString().trim() + '
').appendTo($desc).addClass("codehint-desc-type-details"); + } + } else { + if (hintType[token.kind]) { + $('
' + hintType[token.kind] + '
').appendTo($desc).addClass("codehint-desc-type-details"); + } + } + if (token.documentation) { + $('
').html(token.documentation.trim()).appendTo($desc).addClass("codehint-desc-documentation"); + } + + //To ensure CSS reflow doesn't cause a flicker. + $hintDescContainer.empty(); + $hintDescContainer.append($desc); + }; + exports.CodeHintsProvider = CodeHintsProvider; }); diff --git a/src/styles/brackets_core_ui_variables.less b/src/styles/brackets_core_ui_variables.less index 3824aa242b2..f6290ce0c0f 100644 --- a/src/styles/brackets_core_ui_variables.less +++ b/src/styles/brackets_core_ui_variables.less @@ -124,7 +124,10 @@ @button-icon: "images/find-replace-sprites.svg"; @jstree-sprite: url("images/jsTreeSprites.svg") !important; - +// Codehint description +@bc-codehint-desc: #e6e6e6; +@bc-codehint-desc-type-details: #1473e6; +@bc-codehint-desc-documentation:#424242; /* Dark Core UI variables -----------------------------------------------------------------------------*/ @@ -213,3 +216,8 @@ // images @dark-button-icon: "images/find-replace-sprites-dark.svg"; @dark-jstree-sprite: url("images/jsTreeSprites-dark.svg") !important; + +// Codehint description +@dark-bc-codehint-desc: #2c2c2c; +@dark-bc-codehint-desc-type-details: #46a0f5; +@dark-bc-codehint-desc-documentation:#b1b1b1; diff --git a/src/styles/brackets_patterns_override.less b/src/styles/brackets_patterns_override.less index 44c2879a7a6..e1de2ba3902 100644 --- a/src/styles/brackets_patterns_override.less +++ b/src/styles/brackets_patterns_override.less @@ -584,6 +584,48 @@ a:focus { transition: right 167ms, left 167ms; } +#codehint-desc { + background: @bc-codehint-desc; + position: relative; + width: 100%; + box-sizing: border-box; + float: left; + top: 34px; + border-radius: 1px !important; + border-top-left-radius: 0px !important; + border-top-right-radius: 0px !important; + overflow-x: hidden; + min-height: 30px; + max-height: 120px !important; + + .dark & { + background: @dark-bc-codehint-desc; + } +} + +.codehint-desc-type-details { + padding: 5px 15px 5px 15px; + color: @bc-codehint-desc-type-details; + font-weight: bold; + font-size: 1.2em; + line-height: inherit; + + .dark & { + color: @dark-bc-codehint-desc-type-details; + } +} + +.codehint-desc-documentation { + padding: 5px 15px 5px 15px; + color: @bc-codehint-desc-documentation; + font-size: 1.1em; + white-space: pre-wrap; + + .dark & { + color: @dark-bc-codehint-desc-documentation; + } +} + #context-menu-bar { margin: 0; }