OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 /** |
| 6 * @constructor |
| 7 * @extends {WebInspector.Widget} |
| 8 */ |
| 9 WebInspector.ConsolePrompt = function() |
| 10 { |
| 11 WebInspector.Widget.call(this); |
| 12 this._addCompletionsFromHistory = true; |
| 13 this._history = new WebInspector.HistoryManager(); |
| 14 |
| 15 this._initialText = ""; |
| 16 this._editor = null; |
| 17 |
| 18 this.element.tabIndex = 0; |
| 19 this.element.addEventListener("keydown", this._editorKeyDown.bind(this), tru
e); |
| 20 |
| 21 self.runtime.extension(WebInspector.TextEditorFactory).instance().then(gotFa
ctory.bind(this)); |
| 22 |
| 23 /** |
| 24 * @param {!WebInspector.TextEditorFactory} factory |
| 25 * @this {WebInspector.ConsolePrompt} |
| 26 */ |
| 27 function gotFactory(factory) |
| 28 { |
| 29 this._editor = factory.createEditor({ |
| 30 lineNumbers: false, |
| 31 lineWrapping: true, |
| 32 mimeType: "javascript", |
| 33 autoHeight: true |
| 34 }); |
| 35 |
| 36 this._editor.configureAutocomplete({ |
| 37 substituteRangeCallback: this._substituteRange.bind(this), |
| 38 suggestionsCallback: this._wordsWithPrefix.bind(this), |
| 39 captureEnter: true |
| 40 }) |
| 41 this._editor.widget().show(this.element); |
| 42 |
| 43 this.setText(this._initialText); |
| 44 delete this._initialText; |
| 45 if (this.hasFocus()) |
| 46 this.focus(); |
| 47 this.element.tabIndex = -1; |
| 48 } |
| 49 } |
| 50 |
| 51 WebInspector.ConsolePrompt.prototype = { |
| 52 /** |
| 53 * @return {!WebInspector.HistoryManager} |
| 54 */ |
| 55 history: function() |
| 56 { |
| 57 return this._history; |
| 58 }, |
| 59 |
| 60 clearAutocomplete: function() |
| 61 { |
| 62 if (this._editor) |
| 63 this._editor.clearAutocomplete(); |
| 64 }, |
| 65 |
| 66 /** |
| 67 * @return {boolean} |
| 68 */ |
| 69 isCaretInsidePrompt: function() |
| 70 { |
| 71 return this.hasFocus(); |
| 72 }, |
| 73 |
| 74 /** |
| 75 * @return {boolean} |
| 76 */ |
| 77 isCaretAtEndOfPrompt: function() |
| 78 { |
| 79 return !!this._editor && this._editor.selection().collapseToEnd().equal(
this._editor.fullRange().collapseToEnd()); |
| 80 }, |
| 81 |
| 82 /** |
| 83 * @return {boolean} |
| 84 */ |
| 85 isCaretOnLastLine: function() |
| 86 { |
| 87 return !!this._editor && this._editor.selection().endLine === this._edit
or.fullRange().endLine; |
| 88 }, |
| 89 |
| 90 /** |
| 91 * @return {boolean} |
| 92 */ |
| 93 isCaretOnFirstLine: function() |
| 94 { |
| 95 return !!this._editor && this._editor.selection().endLine === 0; |
| 96 }, |
| 97 |
| 98 moveCaretToEndOfPrompt: function() |
| 99 { |
| 100 if (this._editor) |
| 101 this._editor.setSelection(WebInspector.TextRange.createFromLocation(
Infinity,Infinity)); |
| 102 }, |
| 103 |
| 104 moveCaretToEndOfFirstLine: function() |
| 105 { |
| 106 if (this._editor) |
| 107 this._editor.setSelection(WebInspector.TextRange.createFromLocation(
0,Infinity)); |
| 108 }, |
| 109 |
| 110 /** |
| 111 * @param {string} text |
| 112 */ |
| 113 setText: function(text) |
| 114 { |
| 115 if (this._editor) |
| 116 this._editor.setText(text); |
| 117 else |
| 118 this._initialText = text; |
| 119 }, |
| 120 |
| 121 /** |
| 122 * @return {string} |
| 123 */ |
| 124 text: function() |
| 125 { |
| 126 return this._editor ? this._editor.text() : this._initialText; |
| 127 }, |
| 128 |
| 129 /** |
| 130 * @param {boolean} value |
| 131 */ |
| 132 setAddCompletionsFromHistory: function(value) |
| 133 { |
| 134 this._addCompletionsFromHistory = value; |
| 135 }, |
| 136 |
| 137 /** |
| 138 * @param {!Event} event |
| 139 */ |
| 140 _editorKeyDown: function(event) |
| 141 { |
| 142 var keyboardEvent = /** @type {!KeyboardEvent} */ (event); |
| 143 var newText; |
| 144 var isPrevious; |
| 145 |
| 146 switch (keyboardEvent.keyCode) { |
| 147 case WebInspector.KeyboardShortcut.Keys.Up.code: |
| 148 if (!this.isCaretOnFirstLine()) |
| 149 break; |
| 150 newText = this._history.previous(this.text()); |
| 151 isPrevious = true; |
| 152 break; |
| 153 case WebInspector.KeyboardShortcut.Keys.Down.code: |
| 154 if (!this.isCaretOnLastLine()) |
| 155 break; |
| 156 newText = this._history.next(); |
| 157 break; |
| 158 case WebInspector.KeyboardShortcut.Keys.P.code: // Ctrl+P = Previous |
| 159 if (WebInspector.isMac() && keyboardEvent.ctrlKey && !keyboardEvent.
metaKey && !keyboardEvent.altKey && !keyboardEvent.shiftKey) { |
| 160 newText = this._history.previous(this.text()); |
| 161 isPrevious = true; |
| 162 } |
| 163 break; |
| 164 case WebInspector.KeyboardShortcut.Keys.N.code: // Ctrl+N = Next |
| 165 if (WebInspector.isMac() && keyboardEvent.ctrlKey && !keyboardEvent.
metaKey && !keyboardEvent.altKey && !keyboardEvent.shiftKey) |
| 166 newText = this._history.next(); |
| 167 break; |
| 168 } |
| 169 |
| 170 if (newText === undefined) |
| 171 return; |
| 172 keyboardEvent.consume(true); |
| 173 this.setText(newText); |
| 174 |
| 175 if (isPrevious) |
| 176 this.moveCaretToEndOfFirstLine(); |
| 177 else |
| 178 this.moveCaretToEndOfPrompt(); |
| 179 }, |
| 180 |
| 181 /** |
| 182 * @param {string} prefix |
| 183 * @return {!WebInspector.SuggestBox.Suggestions} |
| 184 */ |
| 185 _historyCompletions: function(prefix) |
| 186 { |
| 187 if (!this._addCompletionsFromHistory || !this.isCaretAtEndOfPrompt()) |
| 188 return []; |
| 189 var result = []; |
| 190 var text = this.text(); |
| 191 var set = new Set(); |
| 192 var data = this._history.historyData(); |
| 193 for (var i = data.length - 1; i >= 0 && result.length < 50; --i) { |
| 194 var item = data[i]; |
| 195 if (!item.startsWith(text)) |
| 196 continue; |
| 197 if (set.has(item)) |
| 198 continue; |
| 199 set.add(item); |
| 200 result.push({title: item.substring(text.length - prefix.length), cla
ssName: "additional"}); |
| 201 } |
| 202 return result; |
| 203 }, |
| 204 |
| 205 /** |
| 206 * @override |
| 207 */ |
| 208 focus: function() |
| 209 { |
| 210 if (this._editor) |
| 211 this._editor.widget().focus(); |
| 212 else |
| 213 this.element.focus(); |
| 214 }, |
| 215 |
| 216 /** |
| 217 * @param {number} lineNumber |
| 218 * @param {number} columnNumber |
| 219 * @return {?WebInspector.TextRange} |
| 220 */ |
| 221 _substituteRange: function(lineNumber, columnNumber) |
| 222 { |
| 223 var lineText = this._editor.line(lineNumber); |
| 224 var index; |
| 225 for (index = lineText.length - 1; index >= 0; index--) { |
| 226 if (" =:[({;,!+-*/&|^<>.".indexOf(lineText.charAt(index)) !== -1) |
| 227 break; |
| 228 } |
| 229 return new WebInspector.TextRange(lineNumber, index + 1, lineNumber, col
umnNumber); |
| 230 }, |
| 231 |
| 232 /** |
| 233 * @param {!WebInspector.TextRange} prefixRange |
| 234 * @param {!WebInspector.TextRange} substituteRange |
| 235 * @return {!Promise.<!Array.<{title: string, className: (string|undefined)}
>>} |
| 236 */ |
| 237 _wordsWithPrefix: function(prefixRange, substituteRange) |
| 238 { |
| 239 var fulfill; |
| 240 var promise = new Promise(x => fulfill = x); |
| 241 var prefix = this._editor.text(prefixRange); |
| 242 var before = this._editor.text(new WebInspector.TextRange(0, 0, prefixRa
nge.startLine, prefixRange.startColumn)); |
| 243 var historyWords = this._historyCompletions(prefix); |
| 244 WebInspector.ExecutionContextSelector.completionsForTextInCurrentContext
(before, prefix, false /* Don't force */, innerWordsWithPrefix); |
| 245 return promise; |
| 246 |
| 247 /** |
| 248 * @param {!Array.<string>} words |
| 249 */ |
| 250 function innerWordsWithPrefix(words) |
| 251 { |
| 252 fulfill(words.map(item => ({title:item})).concat(historyWords)); |
| 253 } |
| 254 }, |
| 255 |
| 256 __proto__: WebInspector.Widget.prototype |
| 257 } |
OLD | NEW |