-
Notifications
You must be signed in to change notification settings - Fork 7.6k
registerHintProvider api change in Code Hint Manager #2279
Changes from 1 commit
c2347c4
676ca72
53c1d94
ea97d4e
c099f5e
ba32cb5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,11 +38,23 @@ define(function (require, exports, module) { | |
KeyEvent = require("utils/KeyEvent"); | ||
|
||
|
||
var hintProviders = [], | ||
var hintProviders = {}, | ||
hintList, | ||
shouldShowHintsOnChange = false, | ||
triggeredKey = null, | ||
keyDownEditor; | ||
|
||
/** Comparator to sort providers based on their specificity */ | ||
function _providerSort(a, b) { | ||
if (a.specificity === b.specificity) { | ||
return 0; | ||
} | ||
if (a.specificity > b.specificity) { | ||
return -1; | ||
} | ||
if (a.specificity < b.specificity) { | ||
return 1; | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should avoid changing the value (and possibly the type) of parameters, so I think a new var should be used here:
|
||
|
||
/** | ||
* @constructor | ||
|
@@ -257,20 +269,47 @@ define(function (require, exports, module) { | |
* @param {Editor} editor | ||
*/ | ||
CodeHintList.prototype.open = function (editor) { | ||
var self = this; | ||
this.editor = editor; | ||
var self = this, | ||
mode = editor.getModeForSelection(), | ||
enabledProviders = []; | ||
|
||
mode = (typeof mode === "string") ? mode : mode.name; | ||
enabledProviders = hintProviders[mode]; | ||
this.editor = editor; | ||
Menus.closeAll(); | ||
|
||
// If we have any providers for "all" mode, then append it to enabled | ||
// porviders list and sort them based on their specificity again. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo: porviders |
||
if (enabledProviders && hintProviders.all) { | ||
enabledProviders = enabledProviders.concat(hintProviders.all); | ||
enabledProviders.sort(_providerSort); | ||
} else if (hintProviders.all) { | ||
enabledProviders = hintProviders.all; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Both conditions check the hintProviders.all flag, so this should be checked only once. This also makes code more clear:
|
||
|
||
if (!enabledProviders) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. enabledProviders is a variable declared as an empty array, so it will always be truthy, I think this should check: |
||
return; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
this.currentProvider = null; | ||
$.each(hintProviders, function (index, item) { | ||
var query = item.getQueryInfo(self.editor, self.editor.getCursorPos()); | ||
$.each(enabledProviders, function (index, item) { | ||
// If we have a triggered key, then skip all the providers that | ||
// do not want to be invoked by the triggered key. | ||
if (triggeredKey && item.provider.triggeredKeys && | ||
item.provider.triggeredKeys.indexOf(triggeredKey) === -1) { | ||
return true; | ||
} | ||
|
||
var query = item.provider.getQueryInfo(self.editor, self.editor.getCursorPos()); | ||
if (query.queryStr !== null) { | ||
self.query = query; | ||
self.currentProvider = item; | ||
self.currentProvider = item.provider; | ||
return false; | ||
} | ||
}); | ||
|
||
triggeredKey = null; | ||
|
||
if (!this.currentProvider) { | ||
return; | ||
} | ||
|
@@ -284,7 +323,7 @@ define(function (require, exports, module) { | |
var hintPos = this.calcHintListLocation(); | ||
|
||
this.$hintMenu.addClass("open") | ||
.css({"left": hintPos.left, "top": hintPos.top}); | ||
.css({"left": hintPos.left, "top": hintPos.top}); | ||
this.opened = true; | ||
|
||
PopUpManager.addPopUp(this.$hintMenu, | ||
|
@@ -311,7 +350,7 @@ define(function (require, exports, module) { | |
this.$hintMenu.remove(); | ||
if (hintList === this) { | ||
hintList = null; | ||
shouldShowHintsOnChange = false; | ||
triggeredKey = null; | ||
keyDownEditor = null; | ||
} | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
@@ -386,24 +425,29 @@ define(function (require, exports, module) { | |
* @param {KeyboardEvent} event | ||
*/ | ||
function handleKeyEvent(editor, event) { | ||
var provider = null; | ||
var provider = null, | ||
mode; | ||
|
||
// Check for Control+Space | ||
if (event.type === "keydown" && event.keyCode === 32 && event.ctrlKey) { | ||
showHint(editor); | ||
event.preventDefault(); | ||
} else if (event.type === "keypress") { | ||
// Check if any provider wants to show hints on this key. | ||
$.each(hintProviders, function (index, item) { | ||
if (item.shouldShowHintsOnKey(String.fromCharCode(event.charCode))) { | ||
provider = item; | ||
return false; | ||
mode = editor.getModeForSelection(); | ||
mode = (typeof mode === "string") ? mode : mode.name; | ||
|
||
if (hintProviders[mode]) { | ||
// Check if any provider wants to show hints on this key. | ||
$.each(hintProviders[mode], function (index, item) { | ||
if (item.triggerKeys && item.triggerKeys.indexOf(String.fromCharCode(event.charCode)) !== -1) { | ||
triggeredKey = String.fromCharCode(event.charCode); | ||
return false; | ||
} | ||
}); | ||
|
||
if (triggeredKey) { | ||
keyDownEditor = editor; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if the hint list ought to be closed when there are no triggered hint providers. I see the following scenario with the in-progress JavaScript provider.
Instead, it seems like the list should be closed when shouldShowHintsOnKey returns false and should only be opened again when that method returns true and provides a non-empty list of hints. Although the provider could attempt to detect this from within getQueryInfo by observing that the cursor is surrounded by whitespace, this would make it tough to handle explicit requests (via ctrl-space) for the hint list. Also note that handleSelect is not called in this scenario, so that method's return value doesn't have a chance to affect the list's status. Or is there another way I haven't considered for the JavaScript provider to handle this scenario? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to follow-up after an offline discussion, I think the simplest change that would solve the problem from the perspective of the JS Hint provider would be to add the last-pressed key as an extra parameter to getQueryInfo. And perhaps that parameter could be null if the hint list was explicitly requested. That would allow the provider to determine whether or not to present a full list of suggestions (on, e.g., ctrl-space) or an empty list (on, e.g., space). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that additional parameter to getQueryInfo is a practical solution to your JS hinting. I can think of the following three different cases that CHM calls getQueryInfo of the current provider.
|
||
}); | ||
|
||
shouldShowHintsOnChange = !!provider; | ||
if (shouldShowHintsOnChange) { | ||
keyDownEditor = editor; | ||
} | ||
} | ||
|
||
|
@@ -417,8 +461,7 @@ define(function (require, exports, module) { | |
* | ||
*/ | ||
function handleChange(editor) { | ||
if (shouldShowHintsOnChange && keyDownEditor === editor) { | ||
shouldShowHintsOnChange = false; | ||
if (triggeredKey && keyDownEditor === editor) { | ||
keyDownEditor = null; | ||
showHint(editor); | ||
} | ||
|
@@ -434,8 +477,7 @@ define(function (require, exports, module) { | |
* | ||
* @param {Object.< getQueryInfo: function(editor, cursor), | ||
* search: function(string), | ||
* handleSelect: function(string, Editor, cursor), | ||
* shouldShowHintsOnKey: function(string)>} | ||
* handleSelect: function(string, Editor, cursor)>} | ||
* | ||
* Parameter Details: | ||
* - getQueryInfo - examines cursor location of editor and returns an object representing | ||
|
@@ -446,10 +488,32 @@ define(function (require, exports, module) { | |
* - handleSelect - takes a completion string and inserts it into the editor near the cursor | ||
* position. It should return true by default to close the hint list, but if the code hint provider | ||
* can return false if it wants to keep the hint list open and continue with a updated list. | ||
* - shouldShowHintsOnKey - inspects the char code and returns true if it wants to show code hints on that key. | ||
* | ||
* @param {Array.<string>} modes An array of mode strings in which the provider can show code hints or "all" | ||
* if it can show code hints in any mode. | ||
* @param {!Array.<string>} triggerKeys An array of all the keys that the provider can be triggered to show hints. | ||
* @param {number} specificity A positive number to indicate the priority of the provider. The larger the number, | ||
* the higher priority the provider has. Zero if it has the lowest priority in displaying its code hints. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This API change might break extensions that use it. Jochen's Spell Checker (https://github.com/couzteau/SpellCheck) is the only one I am aware of that uses it, but this change should be announced on the brackets-dev forum in case there are others. |
||
*/ | ||
function registerHintProvider(providerInfo) { | ||
hintProviders.push(providerInfo); | ||
function registerHintProvider(providerInfo, modes, triggerKeys, specificity) { | ||
var providerObj = { provider: providerInfo, | ||
triggerKeys: triggerKeys || [], | ||
specificity: specificity || 0 }; | ||
|
||
if (modes) { | ||
modes.forEach(function (mode) { | ||
if (mode) { | ||
if (!hintProviders[mode]) { | ||
hintProviders[mode] = []; | ||
} | ||
hintProviders[mode].push(providerObj); | ||
|
||
if (hintProviders[mode].length > 1) { | ||
hintProviders[mode].sort(_providerSort); | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
|
||
/** | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be much simpler to use:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice suggestion!
Yes, simpler in code, but not so simple in logic. :)