2 var Pos
= CodeMirror
.Pos
;
4 function forEach(arr
, f
) {
5 for (var i
= 0, e
= arr
.length
; i
< e
; ++i
) f(arr
[i
]);
8 function arrayContains(arr
, item
) {
9 if (!Array
.prototype.indexOf
) {
12 if (arr
[i
] === item
) {
18 return arr
.indexOf(item
) != -1;
21 function scriptHint(editor
, keywords
, getToken
, options
) {
22 // Find the token at the cursor
23 var cur
= editor
.getCursor(), token
= getToken(editor
, cur
), tprop
= token
;
24 token
.state
= CodeMirror
.innerMode(editor
.getMode(), token
.state
).state
;
26 // If it's not a 'word-style' token, ignore the token.
27 if (!/^[\w$_]*$/.test(token
.string
)) {
28 token
= tprop
= {start
: cur
.ch
, end
: cur
.ch
, string
: "", state
: token
.state
,
29 type
: token
.string
== "." ? "property" : null};
31 // If it is a property, find out what it is a property of.
32 while (tprop
.type
== "property") {
33 tprop
= getToken(editor
, Pos(cur
.line
, tprop
.start
));
34 if (tprop
.string
!= ".") return;
35 tprop
= getToken(editor
, Pos(cur
.line
, tprop
.start
));
36 if (tprop
.string
== ')') {
39 tprop
= getToken(editor
, Pos(cur
.line
, tprop
.start
));
40 switch (tprop
.string
) {
41 case ')': level
++; break;
42 case '(': level
--; break;
46 tprop
= getToken(editor
, Pos(cur
.line
, tprop
.start
));
47 if (tprop
.type
.indexOf("variable") === 0)
48 tprop
.type
= "function";
49 else return; // no clue
51 if (!context
) var context
= [];
54 return {list
: getCompletions(token
, context
, keywords
, options
),
55 from: Pos(cur
.line
, token
.start
),
56 to
: Pos(cur
.line
, token
.end
)};
59 CodeMirror
.javascriptHint = function(editor
, options
) {
60 return scriptHint(editor
, javascriptKeywords
,
61 function (e
, cur
) {return e
.getTokenAt(cur
);},
65 function getCoffeeScriptToken(editor
, cur
) {
66 // This getToken, it is for coffeescript, imitates the behavior of
67 // getTokenAt method in javascript.js, that is, returning "property"
68 // type and treat "." as indepenent token.
69 var token
= editor
.getTokenAt(cur
);
70 if (cur
.ch
== token
.start
+ 1 && token
.string
.charAt(0) == '.') {
71 token
.end
= token
.start
;
73 token
.type
= "property";
75 else if (/^\.[\w$_]*$/.test(token
.string
)) {
76 token
.type
= "property";
78 token
.string
= token
.string
.replace(/\./, '');
83 CodeMirror
.coffeescriptHint = function(editor
, options
) {
84 return scriptHint(editor
, coffeescriptKeywords
, getCoffeeScriptToken
, options
);
87 var stringProps
= ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
88 "toUpperCase toLowerCase split concat match replace search").split(" ");
89 var arrayProps
= ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
90 "lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
91 var funcProps
= "prototype apply call bind".split(" ");
92 var javascriptKeywords
= ("break case catch continue debugger default delete do else false finally for function " +
93 "if in instanceof new null return switch throw true try typeof var void while with").split(" ");
94 var coffeescriptKeywords
= ("and break catch class continue delete do else extends false finally for " +
95 "if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");
97 function getCompletions(token
, context
, keywords
, options
) {
98 var found
= [], start
= token
.string
;
99 function maybeAdd(str
) {
100 if (str
.indexOf(start
) == 0 && !arrayContains(found
, str
)) found
.push(str
);
102 function gatherCompletions(obj
) {
103 if (typeof obj
== "string") forEach(stringProps
, maybeAdd
);
104 else if (obj
instanceof Array
) forEach(arrayProps
, maybeAdd
);
105 else if (obj
instanceof Function
) forEach(funcProps
, maybeAdd
);
106 for (var name
in obj
) maybeAdd(name
);
110 // If this is a property, see if it belongs to some object we can
111 // find in the current environment.
112 var obj
= context
.pop(), base
;
113 if (obj
.type
.indexOf("variable") === 0) {
114 if (options
&& options
.additionalContext
)
115 base
= options
.additionalContext
[obj
.string
];
116 base
= base
|| window
[obj
.string
];
117 } else if (obj
.type
== "string") {
119 } else if (obj
.type
== "atom") {
121 } else if (obj
.type
== "function") {
122 if (window
.jQuery
!= null && (obj
.string
== '$' || obj
.string
== 'jQuery') &&
123 (typeof window
.jQuery
== 'function'))
124 base
= window
.jQuery();
125 else if (window
._
!= null && (obj
.string
== '_') && (typeof window
._
== 'function'))
128 while (base
!= null && context
.length
)
129 base
= base
[context
.pop().string
];
130 if (base
!= null) gatherCompletions(base
);
133 // If not, just look in the window object and any local scope
134 // (reading into JS mode internals to get at the local and global variables)
135 for (var v
= token
.state
.localVars
; v
; v
= v
.next
) maybeAdd(v
.name
);
136 for (var v
= token
.state
.globalVars
; v
; v
= v
.next
) maybeAdd(v
.name
);
137 gatherCompletions(window
);
138 forEach(keywords
, maybeAdd
);