1 CodeMirror
.showHint = function(cm
, getHints
, options
) {
2 if (!options
) options
= {};
3 var startCh
= cm
.getCursor().ch
, continued
= false;
4 var closeOn
= options
.closeCharacters
|| /[\s()\[\]{};:]/;
6 function startHinting() {
7 // We want a single cursor position.
8 if (cm
.somethingSelected()) return;
11 getHints(cm
, showHints
, options
);
13 return showHints(getHints(cm
, options
));
16 function getText(completion
) {
17 if (typeof completion
== "string") return completion
;
18 else return completion
.text
;
21 function pickCompletion(cm
, data
, completion
) {
22 if (completion
.hint
) completion
.hint(cm
, data
, completion
);
23 else cm
.replaceRange(getText(completion
), data
.from, data
.to
);
26 function showHints(data
) {
27 if (!data
|| !data
.list
.length
) return;
28 var completions
= data
.list
;
29 // When there is only one completion, use it directly.
30 if (!continued
&& options
.completeSingle
!== false && completions
.length
== 1) {
31 pickCompletion(cm
, data
, completions
[0]);
32 CodeMirror
.signal(data
, "close");
36 // Build the select widget
37 var hints
= document
.createElement("ul"), selectedHint
= 0;
38 hints
.className
= "CodeMirror-hints";
39 for (var i
= 0; i
< completions
.length
; ++i
) {
40 var elt
= hints
.appendChild(document
.createElement("li")), completion
= completions
[i
];
41 var className
= "CodeMirror-hint" + (i
? "" : " CodeMirror-hint-active");
42 if (completion
.className
!= null) className
= completion
.className
+ " " + className
;
43 elt
.className
= className
;
44 if (completion
.render
) completion
.render(elt
, data
, completion
);
45 else elt
.appendChild(document
.createTextNode(completion
.displayText
|| getText(completion
)));
48 var pos
= cm
.cursorCoords(options
.alignWithWord
!== false ? data
.from : null);
49 var left
= pos
.left
, top
= pos
.bottom
, below
= true;
50 hints
.style
.left
= left
+ "px";
51 hints
.style
.top
= top
+ "px";
52 document
.body
.appendChild(hints
);
53 CodeMirror
.signal(data
, "shown");
55 // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
56 var winW
= window
.innerWidth
|| Math
.max(document
.body
.offsetWidth
, document
.documentElement
.offsetWidth
);
57 var winH
= window
.innerHeight
|| Math
.max(document
.body
.offsetHeight
, document
.documentElement
.offsetHeight
);
58 var box
= hints
.getBoundingClientRect();
59 var overlapX
= box
.right
- winW
, overlapY
= box
.bottom
- winH
;
61 if (box
.right
- box
.left
> winW
) {
62 hints
.style
.width
= (winW
- 5) + "px";
63 overlapX
-= (box
.right
- box
.left
) - winW
;
65 hints
.style
.left
= (left
= pos
.left
- overlapX
) + "px";
68 var height
= box
.bottom
- box
.top
;
69 if (box
.top
- (pos
.bottom
- pos
.top
) - height
> 0) {
70 overlapY
= height
+ (pos
.bottom
- pos
.top
);
72 } else if (height
> winH
) {
73 hints
.style
.height
= (winH
- 5) + "px";
74 overlapY
-= height
- winH
;
76 hints
.style
.top
= (top
= pos
.bottom
- overlapY
) + "px";
79 function changeActive(i
) {
80 i
= Math
.max(0, Math
.min(i
, completions
.length
- 1));
81 if (selectedHint
== i
) return;
82 var node
= hints
.childNodes
[selectedHint
];
83 node
.className
= node
.className
.replace(" CodeMirror-hint-active", "");
84 node
= hints
.childNodes
[selectedHint
= i
];
85 node
.className
+= " CodeMirror-hint-active";
86 if (node
.offsetTop
< hints
.scrollTop
)
87 hints
.scrollTop
= node
.offsetTop
- 3;
88 else if (node
.offsetTop
+ node
.offsetHeight
> hints
.scrollTop
+ hints
.clientHeight
)
89 hints
.scrollTop
= node
.offsetTop
+ node
.offsetHeight
- hints
.clientHeight
+ 3;
90 CodeMirror
.signal(data
, "select", completions
[selectedHint
], node
);
93 function screenAmount() {
94 return Math
.floor(hints
.clientHeight
/ hints
.firstChild
.offsetHeight
) || 1;
97 var ourMap
, baseMap
= {
98 Up: function() {changeActive(selectedHint
- 1);},
99 Down: function() {changeActive(selectedHint
+ 1);},
100 PageUp: function() {changeActive(selectedHint
- screenAmount());},
101 PageDown: function() {changeActive(selectedHint
+ screenAmount());},
102 Home: function() {changeActive(0);},
103 End: function() {changeActive(completions
.length
- 1);},
108 if (options
.customKeys
) {
110 for (var key
in options
.customKeys
) if (options
.customKeys
.hasOwnProperty(key
)) {
111 var val
= options
.customKeys
[key
];
112 if (baseMap
.hasOwnProperty(val
)) val
= baseMap
[val
];
115 } else ourMap
= baseMap
;
117 cm
.addKeyMap(ourMap
);
118 cm
.on("cursorActivity", cursorActivity
);
120 function onBlur(){ closingOnBlur
= setTimeout(close
, 100); };
121 function onFocus(){ clearTimeout(closingOnBlur
); };
122 cm
.on("blur", onBlur
);
123 cm
.on("focus", onFocus
);
124 var startScroll
= cm
.getScrollInfo();
125 function onScroll() {
126 var curScroll
= cm
.getScrollInfo(), editor
= cm
.getWrapperElement().getBoundingClientRect();
127 var newTop
= top
+ startScroll
.top
- curScroll
.top
, point
= newTop
;
128 if (!below
) point
+= hints
.offsetHeight
;
129 if (point
<= editor
.top
|| point
>= editor
.bottom
) return close();
130 hints
.style
.top
= newTop
+ "px";
131 hints
.style
.left
= (left
+ startScroll
.left
- curScroll
.left
) + "px";
133 cm
.on("scroll", onScroll
);
134 CodeMirror
.on(hints
, "dblclick", function(e
) {
135 var t
= e
.target
|| e
.srcElement
;
136 if (t
.hintId
!= null) {selectedHint
= t
.hintId
; pick();}
138 CodeMirror
.on(hints
, "click", function(e
) {
139 var t
= e
.target
|| e
.srcElement
;
140 if (t
.hintId
!= null) changeActive(t
.hintId
);
142 CodeMirror
.on(hints
, "mousedown", function() {
143 setTimeout(function(){cm
.focus();}, 20);
146 var done
= false, once
;
147 function close(willContinue
) {
151 hints
.parentNode
.removeChild(hints
);
152 cm
.removeKeyMap(ourMap
);
153 cm
.off("cursorActivity", cursorActivity
);
154 cm
.off("blur", onBlur
);
155 cm
.off("focus", onFocus
);
156 cm
.off("scroll", onScroll
);
157 if (willContinue
!== true) CodeMirror
.signal(data
, "close");
160 pickCompletion(cm
, data
, completions
[selectedHint
]);
163 var once
, lastPos
= cm
.getCursor(), lastLen
= cm
.getLine(lastPos
.line
).length
;
164 function cursorActivity() {
167 var pos
= cm
.getCursor(), line
= cm
.getLine(pos
.line
);
168 if (pos
.line
!= lastPos
.line
|| line
.length
- pos
.ch
!= lastLen
- lastPos
.ch
||
169 pos
.ch
< startCh
|| cm
.somethingSelected() ||
170 (pos
.ch
&& closeOn
.test(line
.charAt(pos
.ch
- 1))))
173 once
= setTimeout(function(){close(true); continued
= true; startHinting();}, 70);
175 CodeMirror
.signal(data
, "select", completions
[0], hints
.firstChild
);
179 return startHinting();