|
7 | 7 |
|
8 | 8 | // declare global: DOMRect
|
9 | 9 |
|
| 10 | +// The first function (mod) is a wrapper to support different JavaScript environments. |
| 11 | +// The second function (inside) contains the actual implementation. |
| 12 | +import parseCode from './parseCode'; |
| 13 | +import CodeMirror from 'codemirror'; |
| 14 | + |
10 | 15 | (function (mod) {
|
11 | 16 | if (typeof exports == 'object' && typeof module == 'object')
|
12 | 17 | // CommonJS
|
|
16 | 21 | define(['codemirror'], mod);
|
17 | 22 | // Plain browser env
|
18 | 23 | else mod(CodeMirror);
|
| 24 | + |
| 25 | + // This part uptill here makes the code compatible with multiple JavaScript environments, so it can run in different places |
19 | 26 | })(function (CodeMirror) {
|
20 | 27 | 'use strict';
|
21 | 28 |
|
|
25 | 32 | // This is the old interface, kept around for now to stay
|
26 | 33 | // backwards-compatible.
|
27 | 34 | CodeMirror.showHint = function (cm, getHints, options) {
|
28 |
| - if (!getHints) return cm.showHint(options); |
| 35 | + console.log('showhint was called: ', getHints, options); |
| 36 | + if (!getHints) return cm.showHint(options); // if not getHints function passed, it assumes youre using the newer interface |
| 37 | + // restructured options to call the new c.showHint() method |
29 | 38 | if (options && options.async) getHints.async = true;
|
30 | 39 | var newOpts = { hint: getHints };
|
31 | 40 | if (options) for (var prop in options) newOpts[prop] = options[prop];
|
| 41 | + console.log('newopts: ', newOpts); |
| 42 | + const context = parseCode(cm); |
| 43 | + console.log('Cursor context =', context); |
32 | 44 | return cm.showHint(newOpts);
|
33 | 45 | };
|
34 | 46 |
|
| 47 | + // this adds a method called showHint to every cm editor instance (editor.showHint()) |
35 | 48 | CodeMirror.defineExtension('showHint', function (options) {
|
36 | 49 | options = parseOptions(this, this.getCursor('start'), options);
|
37 | 50 | var selections = this.listSelections();
|
| 51 | + console.log('selections are: ', selections); |
38 | 52 | if (selections.length > 1) return;
|
39 | 53 | // By default, don't allow completion when something is selected.
|
40 | 54 | // A hint function can have a `supportsSelection` property to
|
41 | 55 | // indicate that it can handle selections.
|
42 | 56 | if (this.somethingSelected()) {
|
43 | 57 | if (!options.hint.supportsSelection) return;
|
44 | 58 | // Don't try with cross-line selections
|
| 59 | + // if selection spans multiple lines, bail out |
45 | 60 | for (var i = 0; i < selections.length; i++)
|
46 | 61 | if (selections[i].head.line != selections[i].anchor.line) return;
|
47 | 62 | }
|
48 | 63 |
|
49 |
| - if (this.state.completionActive) this.state.completionActive.close(); |
| 64 | + if (this.state.completionActive) this.state.completionActive.close(); // close an already active autocomplete session if active |
| 65 | + // create a new completion object and saves it to this.state.completionActive |
50 | 66 | var completion = (this.state.completionActive = new Completion(
|
51 | 67 | this,
|
52 | 68 | options
|
53 | 69 | ));
|
54 |
| - if (!completion.options.hint) return; |
| 70 | + if (!completion.options.hint) return; // safety check to ensure hint is valid |
55 | 71 |
|
56 |
| - CodeMirror.signal(this, 'startCompletion', this); |
| 72 | + CodeMirror.signal(this, 'startCompletion', this); // emits a signal; fires a startCompletion event on editor instance |
57 | 73 | completion.update(true);
|
58 | 74 | });
|
59 | 75 |
|
60 | 76 | CodeMirror.defineExtension('closeHint', function () {
|
61 | 77 | if (this.state.completionActive) this.state.completionActive.close();
|
62 | 78 | });
|
63 | 79 |
|
| 80 | + // defines a constructor function |
64 | 81 | function Completion(cm, options) {
|
65 | 82 | this.cm = cm;
|
66 | 83 | this.options = options;
|
67 |
| - this.widget = null; |
| 84 | + this.widget = null; // will hold a reference to the dropdown menu that shows suggestions |
68 | 85 | this.debounce = 0;
|
69 | 86 | this.tick = 0;
|
70 |
| - this.startPos = this.cm.getCursor('start'); |
| 87 | + this.startPos = this.cm.getCursor('start'); // startPos is a {line,ch} object used to remember where hinting started |
| 88 | + // startLen is the len of the line minus length of any selected text |
71 | 89 | this.startLen =
|
72 | 90 | this.cm.getLine(this.startPos.line).length -
|
73 | 91 | this.cm.getSelection().length;
|
74 | 92 |
|
75 | 93 | if (this.options.updateOnCursorActivity) {
|
76 |
| - var self = this; |
| 94 | + var self = this; // stores ref to this as self so it can be accessed inside the nested function |
| 95 | + // adds an event listener to the editor; called when the cursor moves |
77 | 96 | cm.on(
|
78 | 97 | 'cursorActivity',
|
79 | 98 | (this.activityFunc = function () {
|
|
95 | 114 | if (!this.active()) return;
|
96 | 115 | this.cm.state.completionActive = null;
|
97 | 116 | this.tick = null;
|
| 117 | + // removes the current activity listener |
98 | 118 | if (this.options.updateOnCursorActivity) {
|
99 | 119 | this.cm.off('cursorActivity', this.activityFunc);
|
100 | 120 | }
|
101 |
| - |
| 121 | + // signals and removes the widget |
102 | 122 | if (this.widget && this.data) CodeMirror.signal(this.data, 'close');
|
103 | 123 | if (this.widget) this.widget.close();
|
| 124 | + // emits a completition end event |
104 | 125 | CodeMirror.signal(this.cm, 'endCompletion', this.cm);
|
105 | 126 | },
|
106 | 127 |
|
|
109 | 130 | },
|
110 | 131 |
|
111 | 132 | pick: function (data, i) {
|
| 133 | + // selects an item from the suggestion list |
112 | 134 | var completion = data.list[i],
|
113 | 135 | self = this;
|
114 | 136 | this.cm.operation(function () {
|
|
120 | 142 | completion.to || data.to,
|
121 | 143 | 'complete'
|
122 | 144 | );
|
| 145 | + // signals that a hint was picked and scrolls to it |
123 | 146 | CodeMirror.signal(data, 'pick', completion);
|
124 | 147 | self.cm.scrollIntoView();
|
125 | 148 | });
|
| 149 | + // closes widget if closeOnPick is enabled |
126 | 150 | if (this.options.closeOnPick) {
|
127 | 151 | this.close();
|
128 | 152 | }
|
129 | 153 | },
|
130 | 154 |
|
131 | 155 | cursorActivity: function () {
|
| 156 | + // if a debounce is scheduled, cancel it to avoid outdated updates |
132 | 157 | if (this.debounce) {
|
133 | 158 | cancelAnimationFrame(this.debounce);
|
134 | 159 | this.debounce = 0;
|
|
149 | 174 | !pos.ch ||
|
150 | 175 | this.options.closeCharacters.test(line.charAt(pos.ch - 1))
|
151 | 176 | ) {
|
| 177 | + console.log('this.close called'); |
152 | 178 | this.close();
|
153 | 179 | } else {
|
154 | 180 | var self = this;
|
|
192 | 218 | function parseOptions(cm, pos, options) {
|
193 | 219 | var editor = cm.options.hintOptions;
|
194 | 220 | var out = {};
|
| 221 | + // copies all default hint settings into out |
195 | 222 | for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
|
196 | 223 | if (editor)
|
197 | 224 | for (var prop in editor)
|
|
200 | 227 | for (var prop in options)
|
201 | 228 | if (options[prop] !== undefined) out[prop] = options[prop];
|
202 | 229 | if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos);
|
| 230 | + console.log('out is ', out); |
203 | 231 | return out;
|
204 | 232 | }
|
205 |
| - |
| 233 | + // extracts the visible text from a completion entry |
206 | 234 | function getText(completion) {
|
207 | 235 | if (typeof completion === 'string') return completion;
|
208 | 236 | else return completion.item.text;
|
209 | 237 | }
|
210 | 238 |
|
| 239 | + // builds a key mapping object to define keyboard behavior for autocomplete |
211 | 240 | function buildKeyMap(completion, handle) {
|
212 | 241 | var baseMap = {
|
213 | 242 | Up: function () {
|
|
232 | 261 | Tab: handle.pick,
|
233 | 262 | Esc: handle.close
|
234 | 263 | };
|
235 |
| - |
| 264 | + // checks if the user is on macOS and adds shortcuts accordingly |
236 | 265 | var mac = /Mac/.test(navigator.platform);
|
237 | 266 |
|
238 | 267 | if (mac) {
|
|
244 | 273 | };
|
245 | 274 | }
|
246 | 275 |
|
| 276 | + // user defined custom key bindings |
247 | 277 | var custom = completion.options.customKeys;
|
248 | 278 | var ourMap = custom ? {} : baseMap;
|
249 | 279 | function addBinding(key, val) {
|
|
257 | 287 | else bound = val;
|
258 | 288 | ourMap[key] = bound;
|
259 | 289 | }
|
| 290 | + // apply all custom key bindings and extraKeys |
260 | 291 | if (custom)
|
261 | 292 | for (var key in custom)
|
262 | 293 | if (custom.hasOwnProperty(key)) addBinding(key, custom[key]);
|
|
267 | 298 | return ourMap;
|
268 | 299 | }
|
269 | 300 |
|
| 301 | + // hintsElement is the parent for hints and el is the clicked element within that container |
270 | 302 | function getHintElement(hintsElement, el) {
|
| 303 | + console.log('el is ', el); |
271 | 304 | while (el && el != hintsElement) {
|
272 |
| - if (el.nodeName.toUpperCase() === 'LI' && el.parentNode == hintsElement) |
| 305 | + if (el.nodeName.toUpperCase() === 'LI' && el.parentNode == hintsElement) { |
| 306 | + console.log('new el is ', el); |
273 | 307 | return el;
|
| 308 | + } |
274 | 309 | el = el.parentNode;
|
275 | 310 | }
|
276 | 311 | }
|
277 | 312 |
|
278 | 313 | function displayHint(name, type, p5) {
|
| 314 | + console.log('name is', name, type, p5); |
279 | 315 | return `<p class="${type}-item">\
|
280 | 316 | <span class="${type}-name hint-name">${name}</span>\
|
281 | 317 | <span class="hint-hidden">, </span>\
|
|
293 | 329 | }
|
294 | 330 |
|
295 | 331 | function getInlineHintSuggestion(focus, tokenLength) {
|
| 332 | + console.log('the focus is: ', focus, tokenLength); |
296 | 333 | const suggestionItem = focus.item;
|
| 334 | + // builds the remainder of the suggestion excluding what user already typed |
297 | 335 | const baseCompletion = `<span class="inline-hinter-suggestion">${suggestionItem.text.slice(
|
298 | 336 | tokenLength
|
299 | 337 | )}</span>`;
|
|
310 | 348 | );
|
311 | 349 | }
|
312 | 350 |
|
| 351 | + // clears existing inline hint (like the part is suggested) |
313 | 352 | function removeInlineHint(cm) {
|
314 | 353 | if (cm.state.inlineHint) {
|
315 | 354 | cm.state.inlineHint.clear();
|
|
341 | 380 | }
|
342 | 381 | }
|
343 | 382 |
|
| 383 | + // defines the autocomplete dropdown ui |
| 384 | + // completition = the autocomplete context having cm and options |
| 385 | + // data = object with the list of suggestions |
344 | 386 | function Widget(completion, data) {
|
| 387 | + console.log('widget completetition= ', completion); |
| 388 | + console.log('widget data= ', data); |
345 | 389 | this.id = 'cm-complete-' + Math.floor(Math.random(1e6));
|
346 | 390 | this.completion = completion;
|
347 | 391 | this.data = data;
|
|
0 commit comments