simple-hint.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. (function() {
  2. CodeMirror.simpleHint = function(editor, getHints, givenOptions) {
  3. // Determine effective options based on given values and defaults.
  4. var options = {}, defaults = CodeMirror.simpleHint.defaults;
  5. for (var opt in defaults)
  6. if (defaults.hasOwnProperty(opt))
  7. options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt];
  8. function collectHints(previousToken) {
  9. // We want a single cursor position.
  10. if (editor.somethingSelected()) return;
  11. var tempToken = editor.getTokenAt(editor.getCursor());
  12. // Don't show completions if token has changed and the option is set.
  13. if (options.closeOnTokenChange && previousToken != null &&
  14. (tempToken.start != previousToken.start || tempToken.type != previousToken.type)) {
  15. return;
  16. }
  17. var result = getHints(editor, givenOptions);
  18. if (!result || !result.list.length) return;
  19. var completions = result.list;
  20. function insert(str) {
  21. editor.replaceRange(str, result.from, result.to);
  22. }
  23. // When there is only one completion, use it directly.
  24. if (options.completeSingle && completions.length == 1) {
  25. insert(completions[0]);
  26. return true;
  27. }
  28. // Build the select widget
  29. var complete = document.createElement("div");
  30. complete.className = "CodeMirror-completions";
  31. var sel = complete.appendChild(document.createElement("select"));
  32. // Opera doesn't move the selection when pressing up/down in a
  33. // multi-select, but it does properly support the size property on
  34. // single-selects, so no multi-select is necessary.
  35. if (!window.opera) sel.multiple = true;
  36. for (var i = 0; i < completions.length; ++i) {
  37. var opt = sel.appendChild(document.createElement("option"));
  38. opt.appendChild(document.createTextNode(completions[i]));
  39. }
  40. sel.firstChild.selected = true;
  41. sel.size = Math.min(10, completions.length);
  42. var pos = editor.cursorCoords(options.alignWithWord ? result.from : null);
  43. complete.style.left = pos.left + "px";
  44. complete.style.top = pos.bottom + "px";
  45. document.body.appendChild(complete);
  46. // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
  47. var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
  48. if(winW - pos.left < sel.clientWidth)
  49. complete.style.left = (pos.left - sel.clientWidth) + "px";
  50. // Hack to hide the scrollbar.
  51. if (completions.length <= 10)
  52. complete.style.width = (sel.clientWidth - 1) + "px";
  53. var done = false;
  54. function close() {
  55. if (done) return;
  56. done = true;
  57. complete.parentNode.removeChild(complete);
  58. }
  59. function pick() {
  60. insert(completions[sel.selectedIndex]);
  61. close();
  62. setTimeout(function(){editor.focus();}, 50);
  63. }
  64. CodeMirror.on(sel, "blur", close);
  65. CodeMirror.on(sel, "keydown", function(event) {
  66. var code = event.keyCode;
  67. // Enter
  68. if (code == 13) {CodeMirror.e_stop(event); pick();}
  69. // Escape
  70. else if (code == 27) {CodeMirror.e_stop(event); close(); editor.focus();}
  71. else if (code != 38 && code != 40 && code != 33 && code != 34 && !CodeMirror.isModifierKey(event)) {
  72. close(); editor.focus();
  73. // Pass the event to the CodeMirror instance so that it can handle things like backspace properly.
  74. editor.triggerOnKeyDown(event);
  75. // Don't show completions if the code is backspace and the option is set.
  76. if (!options.closeOnBackspace || code != 8) {
  77. setTimeout(function(){collectHints(tempToken);}, 50);
  78. }
  79. }
  80. });
  81. CodeMirror.on(sel, "dblclick", pick);
  82. sel.focus();
  83. // Opera sometimes ignores focusing a freshly created node
  84. if (window.opera) setTimeout(function(){if (!done) sel.focus();}, 100);
  85. return true;
  86. }
  87. return collectHints();
  88. };
  89. CodeMirror.simpleHint.defaults = {
  90. closeOnBackspace: true,
  91. closeOnTokenChange: false,
  92. completeSingle: true,
  93. alignWithWord: true
  94. };
  95. })();