1/* 2 * Copyright (C) 2012 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31/** 32 * @constructor 33 * @extends {WebInspector.DialogDelegate} 34 * @implements {WebInspector.ViewportControl.Provider} 35 * @param {!WebInspector.SelectionDialogContentProvider} delegate 36 */ 37WebInspector.FilteredItemSelectionDialog = function(delegate) 38{ 39 WebInspector.DialogDelegate.call(this); 40 41 if (!WebInspector.FilteredItemSelectionDialog._stylesLoaded) { 42 WebInspector.View.createStyleElement("filteredItemSelectionDialog.css"); 43 WebInspector.FilteredItemSelectionDialog._stylesLoaded = true; 44 } 45 46 this.element = document.createElement("div"); 47 this.element.className = "filtered-item-list-dialog"; 48 this.element.addEventListener("keydown", this._onKeyDown.bind(this), false); 49 50 this._promptElement = this.element.createChild("input", "monospace"); 51 this._promptElement.addEventListener("input", this._onInput.bind(this), false); 52 this._promptElement.type = "text"; 53 this._promptElement.setAttribute("spellcheck", "false"); 54 55 this._filteredItems = []; 56 this._viewportControl = new WebInspector.ViewportControl(this); 57 this._viewportControl.element.classList.add("fill"); 58 this._itemElementsContainer = this._viewportControl.element; 59 this._itemElementsContainer.classList.add("container"); 60 this._itemElementsContainer.classList.add("monospace"); 61 this._itemElementsContainer.addEventListener("click", this._onClick.bind(this), false); 62 this.element.appendChild(this._itemElementsContainer); 63 64 this._delegate = delegate; 65 this._delegate.setRefreshCallback(this._itemsLoaded.bind(this)); 66 this._itemsLoaded(); 67} 68 69WebInspector.FilteredItemSelectionDialog.prototype = { 70 /** 71 * @param {!Element} element 72 * @param {!Element} relativeToElement 73 */ 74 position: function(element, relativeToElement) 75 { 76 const shadow = 10; 77 const shadowPadding = 20; // shadow + padding 78 var container = WebInspector.Dialog.modalHostView().element; 79 var preferredWidth = Math.max(relativeToElement.offsetWidth * 2 / 3, 500); 80 var width = Math.min(preferredWidth, container.offsetWidth - 2 * shadowPadding); 81 var preferredHeight = Math.max(relativeToElement.offsetHeight * 2 / 3, 204); 82 var height = Math.min(preferredHeight, container.offsetHeight - 2 * shadowPadding); 83 84 this.element.style.width = width + "px"; 85 var box = relativeToElement.boxInWindow(window).relativeToElement(container); 86 var positionX = box.x + Math.max((box.width - width - 2 * shadowPadding) / 2, shadow); 87 positionX = Math.max(shadow, Math.min(container.offsetWidth - width - 2 * shadowPadding, positionX)); 88 var positionY = box.y + Math.max((box.height - height - 2 * shadowPadding) / 2, shadow); 89 positionY = Math.max(shadow, Math.min(container.offsetHeight - height - 2 * shadowPadding, positionY)); 90 element.positionAt(positionX, positionY, container); 91 this._dialogHeight = height; 92 93 this._updateShowMatchingItems(); 94 }, 95 96 focus: function() 97 { 98 WebInspector.setCurrentFocusElement(this._promptElement); 99 if (this._filteredItems.length && this._viewportControl.lastVisibleIndex() === -1) 100 this._viewportControl.refresh(); 101 }, 102 103 willHide: function() 104 { 105 if (this._isHiding) 106 return; 107 this._isHiding = true; 108 this._delegate.dispose(); 109 if (this._filterTimer) 110 clearTimeout(this._filterTimer); 111 }, 112 113 renderAsTwoRows: function() 114 { 115 this._renderAsTwoRows = true; 116 }, 117 118 onEnter: function() 119 { 120 if (!this._delegate.itemCount()) 121 return; 122 var selectedIndex = this._shouldShowMatchingItems() && this._selectedIndexInFiltered < this._filteredItems.length ? this._filteredItems[this._selectedIndexInFiltered] : null; 123 this._delegate.selectItem(selectedIndex, this._promptElement.value.trim()); 124 }, 125 126 _itemsLoaded: function() 127 { 128 129 if (this._loadTimeout) 130 return; 131 this._loadTimeout = setTimeout(this._updateAfterItemsLoaded.bind(this), 0); 132 }, 133 134 _updateAfterItemsLoaded: function() 135 { 136 delete this._loadTimeout; 137 this._filterItems(); 138 }, 139 140 /** 141 * @param {number} index 142 * @return {!Element} 143 */ 144 _createItemElement: function(index) 145 { 146 var itemElement = document.createElement("div"); 147 itemElement.className = "filtered-item-list-dialog-item " + (this._renderAsTwoRows ? "two-rows" : "one-row"); 148 itemElement._titleElement = itemElement.createChild("div", "filtered-item-list-dialog-title"); 149 itemElement._subtitleElement = itemElement.createChild("div", "filtered-item-list-dialog-subtitle"); 150 itemElement._subtitleElement.textContent = "\u200B"; 151 itemElement._index = index; 152 this._delegate.renderItem(index, this._promptElement.value.trim(), itemElement._titleElement, itemElement._subtitleElement); 153 return itemElement; 154 }, 155 156 /** 157 * @param {string} query 158 */ 159 setQuery: function(query) 160 { 161 this._promptElement.value = query; 162 this._scheduleFilter(); 163 }, 164 165 _filterItems: function() 166 { 167 delete this._filterTimer; 168 if (this._scoringTimer) { 169 clearTimeout(this._scoringTimer); 170 delete this._scoringTimer; 171 } 172 173 var query = this._delegate.rewriteQuery(this._promptElement.value.trim()); 174 this._query = query; 175 var queryLength = query.length; 176 var filterRegex = query ? WebInspector.FilePathScoreFunction.filterRegex(query) : null; 177 178 var oldSelectedAbsoluteIndex = this._selectedIndexInFiltered ? this._filteredItems[this._selectedIndexInFiltered] : null; 179 var filteredItems = []; 180 this._selectedIndexInFiltered = 0; 181 182 var bestScores = []; 183 var bestItems = []; 184 var bestItemsToCollect = 100; 185 var minBestScore = 0; 186 var overflowItems = []; 187 188 scoreItems.call(this, 0); 189 190 /** 191 * @param {number} a 192 * @param {number} b 193 * @return {number} 194 */ 195 function compareIntegers(a, b) 196 { 197 return b - a; 198 } 199 200 /** 201 * @param {number} fromIndex 202 * @this {WebInspector.FilteredItemSelectionDialog} 203 */ 204 function scoreItems(fromIndex) 205 { 206 var maxWorkItems = 1000; 207 var workDone = 0; 208 for (var i = fromIndex; i < this._delegate.itemCount() && workDone < maxWorkItems; ++i) { 209 // Filter out non-matching items quickly. 210 if (filterRegex && !filterRegex.test(this._delegate.itemKeyAt(i))) 211 continue; 212 213 // Score item. 214 var score = this._delegate.itemScoreAt(i, query); 215 if (query) 216 workDone++; 217 218 // Find its index in the scores array (earlier elements have bigger scores). 219 if (score > minBestScore || bestScores.length < bestItemsToCollect) { 220 var index = insertionIndexForObjectInListSortedByFunction(score, bestScores, compareIntegers, true); 221 bestScores.splice(index, 0, score); 222 bestItems.splice(index, 0, i); 223 if (bestScores.length > bestItemsToCollect) { 224 // Best list is too large -> drop last elements. 225 overflowItems.push(bestItems.peekLast()); 226 bestScores.length = bestItemsToCollect; 227 bestItems.length = bestItemsToCollect; 228 } 229 minBestScore = bestScores.peekLast(); 230 } else 231 filteredItems.push(i); 232 } 233 234 // Process everything in chunks. 235 if (i < this._delegate.itemCount()) { 236 this._scoringTimer = setTimeout(scoreItems.bind(this, i), 0); 237 return; 238 } 239 delete this._scoringTimer; 240 241 this._filteredItems = bestItems.concat(overflowItems).concat(filteredItems); 242 for (var i = 0; i < this._filteredItems.length; ++i) { 243 if (this._filteredItems[i] === oldSelectedAbsoluteIndex) { 244 this._selectedIndexInFiltered = i; 245 break; 246 } 247 } 248 this._viewportControl.invalidate(); 249 if (!query) 250 this._selectedIndexInFiltered = 0; 251 this._updateSelection(this._selectedIndexInFiltered, false); 252 } 253 }, 254 255 /** 256 * @return {boolean} 257 */ 258 _shouldShowMatchingItems: function() 259 { 260 return this._delegate.shouldShowMatchingItems(this._promptElement.value); 261 }, 262 263 _onInput: function(event) 264 { 265 this._updateShowMatchingItems(); 266 this._scheduleFilter(); 267 }, 268 269 _updateShowMatchingItems: function() 270 { 271 var shouldShowMatchingItems = this._shouldShowMatchingItems(); 272 this._itemElementsContainer.classList.toggle("hidden", !shouldShowMatchingItems); 273 this.element.style.height = shouldShowMatchingItems ? this._dialogHeight + "px" : "auto"; 274 }, 275 276 /** 277 * @return {number} 278 */ 279 _rowsPerViewport: function() 280 { 281 return Math.floor(this._viewportControl.element.clientHeight / this._rowHeight); 282 }, 283 284 _onKeyDown: function(event) 285 { 286 var newSelectedIndex = this._selectedIndexInFiltered; 287 288 switch (event.keyCode) { 289 case WebInspector.KeyboardShortcut.Keys.Down.code: 290 if (++newSelectedIndex >= this._filteredItems.length) 291 newSelectedIndex = this._filteredItems.length - 1; 292 this._updateSelection(newSelectedIndex, true); 293 event.consume(true); 294 break; 295 case WebInspector.KeyboardShortcut.Keys.Up.code: 296 if (--newSelectedIndex < 0) 297 newSelectedIndex = 0; 298 this._updateSelection(newSelectedIndex, false); 299 event.consume(true); 300 break; 301 case WebInspector.KeyboardShortcut.Keys.PageDown.code: 302 newSelectedIndex = Math.min(newSelectedIndex + this._rowsPerViewport(), this._filteredItems.length - 1); 303 this._updateSelection(newSelectedIndex, true); 304 event.consume(true); 305 break; 306 case WebInspector.KeyboardShortcut.Keys.PageUp.code: 307 newSelectedIndex = Math.max(newSelectedIndex - this._rowsPerViewport(), 0); 308 this._updateSelection(newSelectedIndex, false); 309 event.consume(true); 310 break; 311 default: 312 } 313 }, 314 315 _scheduleFilter: function() 316 { 317 if (this._filterTimer) 318 return; 319 this._filterTimer = setTimeout(this._filterItems.bind(this), 0); 320 }, 321 322 /** 323 * @param {number} index 324 * @param {boolean} makeLast 325 */ 326 _updateSelection: function(index, makeLast) 327 { 328 var element = this._viewportControl.renderedElementAt(this._selectedIndexInFiltered); 329 if (element) 330 element.classList.remove("selected"); 331 this._viewportControl.scrollItemIntoView(index, makeLast); 332 this._selectedIndexInFiltered = index; 333 element = this._viewportControl.renderedElementAt(index); 334 if (element) 335 element.classList.add("selected"); 336 }, 337 338 _onClick: function(event) 339 { 340 var itemElement = event.target.enclosingNodeOrSelfWithClass("filtered-item-list-dialog-item"); 341 if (!itemElement) 342 return; 343 this._delegate.selectItem(itemElement._index, this._promptElement.value.trim()); 344 WebInspector.Dialog.hide(); 345 }, 346 347 /** 348 * @return {number} 349 */ 350 itemCount: function() 351 { 352 return this._filteredItems.length; 353 }, 354 355 /** 356 * @param {number} index 357 * @return {number} 358 */ 359 fastHeight: function(index) 360 { 361 if (!this._rowHeight) { 362 var delegateIndex = this._filteredItems[index]; 363 var element = this._createItemElement(delegateIndex); 364 this._rowHeight = element.measurePreferredSize(this._viewportControl.contentElement()).height; 365 } 366 return this._rowHeight; 367 }, 368 369 /** 370 * @param {number} index 371 * @return {!WebInspector.ViewportElement} 372 */ 373 itemElement: function(index) 374 { 375 var delegateIndex = this._filteredItems[index]; 376 var element = this._createItemElement(delegateIndex); 377 if (index === this._selectedIndexInFiltered) 378 element.classList.add("selected"); 379 return new WebInspector.StaticViewportElement(element); 380 }, 381 382 /** 383 * @return {number} 384 */ 385 minimumRowHeight: function() 386 { 387 return this.fastHeight(0); 388 }, 389 390 __proto__: WebInspector.DialogDelegate.prototype 391} 392 393/** 394 * @constructor 395 */ 396WebInspector.SelectionDialogContentProvider = function() 397{ 398} 399 400WebInspector.SelectionDialogContentProvider.prototype = { 401 /** 402 * @param {function():void} refreshCallback 403 */ 404 setRefreshCallback: function(refreshCallback) 405 { 406 this._refreshCallback = refreshCallback; 407 }, 408 409 /** 410 * @param {string} query 411 * @return {boolean} 412 */ 413 shouldShowMatchingItems: function(query) 414 { 415 return true; 416 }, 417 418 /** 419 * @return {number} 420 */ 421 itemCount: function() 422 { 423 return 0; 424 }, 425 426 /** 427 * @param {number} itemIndex 428 * @return {string} 429 */ 430 itemKeyAt: function(itemIndex) 431 { 432 return ""; 433 }, 434 435 /** 436 * @param {number} itemIndex 437 * @param {string} query 438 * @return {number} 439 */ 440 itemScoreAt: function(itemIndex, query) 441 { 442 return 1; 443 }, 444 445 /** 446 * @param {number} itemIndex 447 * @param {string} query 448 * @param {!Element} titleElement 449 * @param {!Element} subtitleElement 450 */ 451 renderItem: function(itemIndex, query, titleElement, subtitleElement) 452 { 453 }, 454 455 /** 456 * @param {!Element} element 457 * @param {string} query 458 * @return {boolean} 459 */ 460 highlightRanges: function(element, query) 461 { 462 if (!query) 463 return false; 464 465 /** 466 * @param {string} text 467 * @param {string} query 468 * @return {?Array.<!WebInspector.SourceRange>} 469 */ 470 function rangesForMatch(text, query) 471 { 472 var sm = new difflib.SequenceMatcher(query, text); 473 var opcodes = sm.get_opcodes(); 474 var ranges = []; 475 476 for (var i = 0; i < opcodes.length; ++i) { 477 var opcode = opcodes[i]; 478 if (opcode[0] === "equal") 479 ranges.push(new WebInspector.SourceRange(opcode[3], opcode[4] - opcode[3])); 480 else if (opcode[0] !== "insert") 481 return null; 482 } 483 return ranges; 484 } 485 486 var text = element.textContent; 487 var ranges = rangesForMatch(text, query); 488 if (!ranges) 489 ranges = rangesForMatch(text.toUpperCase(), query.toUpperCase()); 490 if (ranges) { 491 WebInspector.highlightRangesWithStyleClass(element, ranges, "highlight"); 492 return true; 493 } 494 return false; 495 }, 496 497 /** 498 * @param {?number} itemIndex 499 * @param {string} promptValue 500 */ 501 selectItem: function(itemIndex, promptValue) 502 { 503 }, 504 505 refresh: function() 506 { 507 this._refreshCallback(); 508 }, 509 510 /** 511 * @param {string} query 512 * @return {string} 513 */ 514 rewriteQuery: function(query) 515 { 516 return query; 517 }, 518 519 dispose: function() 520 { 521 } 522} 523 524/** 525 * @constructor 526 * @extends {WebInspector.SelectionDialogContentProvider} 527 * @param {!WebInspector.UISourceCode} uiSourceCode 528 * @param {function(number, number)} selectItemCallback 529 */ 530WebInspector.JavaScriptOutlineDialog = function(uiSourceCode, selectItemCallback) 531{ 532 WebInspector.SelectionDialogContentProvider.call(this); 533 534 this._functionItems = []; 535 this._selectItemCallback = selectItemCallback; 536 this._outlineWorker = new Worker("script_formatter_worker/ScriptFormatterWorker.js"); 537 this._outlineWorker.onmessage = this._didBuildOutlineChunk.bind(this); 538 this._outlineWorker.postMessage({ method: "javaScriptOutline", params: { content: uiSourceCode.workingCopy() } }); 539} 540 541/** 542 * @param {!WebInspector.View} view 543 * @param {!WebInspector.UISourceCode} uiSourceCode 544 * @param {function(number, number)} selectItemCallback 545 */ 546WebInspector.JavaScriptOutlineDialog.show = function(view, uiSourceCode, selectItemCallback) 547{ 548 if (WebInspector.Dialog.currentInstance()) 549 return; 550 var filteredItemSelectionDialog = new WebInspector.FilteredItemSelectionDialog(new WebInspector.JavaScriptOutlineDialog(uiSourceCode, selectItemCallback)); 551 WebInspector.Dialog.show(view.element, filteredItemSelectionDialog); 552} 553 554WebInspector.JavaScriptOutlineDialog.prototype = { 555 /** 556 * @param {!MessageEvent} event 557 */ 558 _didBuildOutlineChunk: function(event) 559 { 560 var data = /** @type {!WebInspector.JavaScriptOutlineDialog.MessageEventData} */ (event.data); 561 var chunk = data.chunk; 562 for (var i = 0; i < chunk.length; ++i) 563 this._functionItems.push(chunk[i]); 564 565 if (data.total === data.index + 1) 566 this.dispose(); 567 568 this.refresh(); 569 }, 570 571 /** 572 * @return {number} 573 */ 574 itemCount: function() 575 { 576 return this._functionItems.length; 577 }, 578 579 /** 580 * @param {number} itemIndex 581 * @return {string} 582 */ 583 itemKeyAt: function(itemIndex) 584 { 585 return this._functionItems[itemIndex].name; 586 }, 587 588 /** 589 * @param {number} itemIndex 590 * @param {string} query 591 * @return {number} 592 */ 593 itemScoreAt: function(itemIndex, query) 594 { 595 var item = this._functionItems[itemIndex]; 596 return -item.line; 597 }, 598 599 /** 600 * @param {number} itemIndex 601 * @param {string} query 602 * @param {!Element} titleElement 603 * @param {!Element} subtitleElement 604 */ 605 renderItem: function(itemIndex, query, titleElement, subtitleElement) 606 { 607 var item = this._functionItems[itemIndex]; 608 titleElement.textContent = item.name + (item.arguments ? item.arguments : ""); 609 this.highlightRanges(titleElement, query); 610 subtitleElement.textContent = ":" + (item.line + 1); 611 }, 612 613 /** 614 * @param {?number} itemIndex 615 * @param {string} promptValue 616 */ 617 selectItem: function(itemIndex, promptValue) 618 { 619 if (itemIndex === null) 620 return; 621 var lineNumber = this._functionItems[itemIndex].line; 622 if (!isNaN(lineNumber) && lineNumber >= 0) 623 this._selectItemCallback(lineNumber, this._functionItems[itemIndex].column); 624 }, 625 626 dispose: function() 627 { 628 if (this._outlineWorker) { 629 this._outlineWorker.terminate(); 630 delete this._outlineWorker; 631 } 632 }, 633 634 __proto__: WebInspector.SelectionDialogContentProvider.prototype 635} 636 637/** 638 * @constructor 639 * @extends {WebInspector.SelectionDialogContentProvider} 640 * @param {!Map.<!WebInspector.UISourceCode, number>=} defaultScores 641 */ 642WebInspector.SelectUISourceCodeDialog = function(defaultScores) 643{ 644 WebInspector.SelectionDialogContentProvider.call(this); 645 646 this._populate(); 647 this._defaultScores = defaultScores; 648 this._scorer = new WebInspector.FilePathScoreFunction(""); 649 WebInspector.workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this); 650 WebInspector.workspace.addEventListener(WebInspector.Workspace.Events.ProjectRemoved, this._projectRemoved, this); 651} 652 653WebInspector.SelectUISourceCodeDialog.prototype = { 654 _projectRemoved: function(event) 655 { 656 var project = /** @type {!WebInspector.Project} */ (event.data); 657 this._populate(project); 658 this.refresh(); 659 }, 660 661 /** 662 * @param {!WebInspector.Project=} skipProject 663 */ 664 _populate: function(skipProject) 665 { 666 /** @type {!Array.<!WebInspector.UISourceCode>} */ 667 this._uiSourceCodes = []; 668 var projects = WebInspector.workspace.projects().filter(this.filterProject.bind(this)); 669 for (var i = 0; i < projects.length; ++i) { 670 if (skipProject && projects[i] === skipProject) 671 continue; 672 this._uiSourceCodes = this._uiSourceCodes.concat(projects[i].uiSourceCodes()); 673 } 674 }, 675 676 /** 677 * @param {?WebInspector.UISourceCode} uiSourceCode 678 * @param {number=} lineNumber 679 * @param {number=} columnNumber 680 */ 681 uiSourceCodeSelected: function(uiSourceCode, lineNumber, columnNumber) 682 { 683 // Overridden by subclasses 684 }, 685 686 /** 687 * @param {!WebInspector.Project} project 688 * @return {boolean} 689 */ 690 filterProject: function(project) 691 { 692 return true; 693 // Overridden by subclasses 694 }, 695 696 /** 697 * @return {number} 698 */ 699 itemCount: function() 700 { 701 return this._uiSourceCodes.length; 702 }, 703 704 /** 705 * @param {number} itemIndex 706 * @return {string} 707 */ 708 itemKeyAt: function(itemIndex) 709 { 710 return this._uiSourceCodes[itemIndex].fullDisplayName(); 711 }, 712 713 /** 714 * @param {number} itemIndex 715 * @param {string} query 716 * @return {number} 717 */ 718 itemScoreAt: function(itemIndex, query) 719 { 720 var uiSourceCode = this._uiSourceCodes[itemIndex]; 721 var score = this._defaultScores ? (this._defaultScores.get(uiSourceCode) || 0) : 0; 722 if (!query || query.length < 2) 723 return score; 724 725 if (this._query !== query) { 726 this._query = query; 727 this._scorer = new WebInspector.FilePathScoreFunction(query); 728 } 729 730 var path = uiSourceCode.fullDisplayName(); 731 return score + 10 * this._scorer.score(path, null); 732 }, 733 734 /** 735 * @param {number} itemIndex 736 * @param {string} query 737 * @param {!Element} titleElement 738 * @param {!Element} subtitleElement 739 * @return {!Array.<!Element>} 740 */ 741 renderItem: function(itemIndex, query, titleElement, subtitleElement) 742 { 743 query = this.rewriteQuery(query); 744 var uiSourceCode = this._uiSourceCodes[itemIndex]; 745 titleElement.textContent = uiSourceCode.displayName() + (this._queryLineNumberAndColumnNumber || ""); 746 subtitleElement.textContent = uiSourceCode.fullDisplayName().trimEnd(100); 747 748 var indexes = []; 749 var score = new WebInspector.FilePathScoreFunction(query).score(subtitleElement.textContent, indexes); 750 var fileNameIndex = subtitleElement.textContent.lastIndexOf("/"); 751 var ranges = []; 752 for (var i = 0; i < indexes.length; ++i) 753 ranges.push({offset: indexes[i], length: 1}); 754 if (indexes[0] > fileNameIndex) { 755 for (var i = 0; i < ranges.length; ++i) 756 ranges[i].offset -= fileNameIndex + 1; 757 return WebInspector.highlightRangesWithStyleClass(titleElement, ranges, "highlight"); 758 } else { 759 return WebInspector.highlightRangesWithStyleClass(subtitleElement, ranges, "highlight"); 760 } 761 }, 762 763 /** 764 * @param {?number} itemIndex 765 * @param {string} promptValue 766 */ 767 selectItem: function(itemIndex, promptValue) 768 { 769 var parsedExpression = promptValue.trim().match(/^([^:]*)(:\d+)?(:\d+)?$/); 770 if (!parsedExpression) 771 return; 772 773 var lineNumber; 774 var columnNumber; 775 if (parsedExpression[2]) 776 lineNumber = parseInt(parsedExpression[2].substr(1), 10) - 1; 777 if (parsedExpression[3]) 778 columnNumber = parseInt(parsedExpression[3].substr(1), 10) - 1; 779 var uiSourceCode = itemIndex !== null ? this._uiSourceCodes[itemIndex] : null; 780 this.uiSourceCodeSelected(uiSourceCode, lineNumber, columnNumber); 781 }, 782 783 /** 784 * @param {string} query 785 * @return {string} 786 */ 787 rewriteQuery: function(query) 788 { 789 if (!query) 790 return query; 791 query = query.trim(); 792 var lineNumberMatch = query.match(/^([^:]+)((?::[^:]*){0,2})$/); 793 this._queryLineNumberAndColumnNumber = lineNumberMatch ? lineNumberMatch[2] : ""; 794 return lineNumberMatch ? lineNumberMatch[1] : query; 795 }, 796 797 /** 798 * @param {!WebInspector.Event} event 799 */ 800 _uiSourceCodeAdded: function(event) 801 { 802 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data); 803 if (!this.filterProject(uiSourceCode.project())) 804 return; 805 this._uiSourceCodes.push(uiSourceCode) 806 this.refresh(); 807 }, 808 809 dispose: function() 810 { 811 WebInspector.workspace.removeEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this); 812 WebInspector.workspace.removeEventListener(WebInspector.Workspace.Events.ProjectRemoved, this._projectRemoved, this); 813 }, 814 815 __proto__: WebInspector.SelectionDialogContentProvider.prototype 816} 817 818/** 819 * @constructor 820 * @extends {WebInspector.SelectUISourceCodeDialog} 821 * @param {!WebInspector.SourcesView} sourcesView 822 * @param {!Map.<!WebInspector.UISourceCode, number>=} defaultScores 823 */ 824WebInspector.OpenResourceDialog = function(sourcesView, defaultScores) 825{ 826 WebInspector.SelectUISourceCodeDialog.call(this, defaultScores); 827 this._sourcesView = sourcesView; 828} 829 830WebInspector.OpenResourceDialog.prototype = { 831 832 /** 833 * @param {?WebInspector.UISourceCode} uiSourceCode 834 * @param {number=} lineNumber 835 * @param {number=} columnNumber 836 */ 837 uiSourceCodeSelected: function(uiSourceCode, lineNumber, columnNumber) 838 { 839 if (!uiSourceCode) 840 uiSourceCode = this._sourcesView.currentUISourceCode(); 841 if (!uiSourceCode) 842 return; 843 this._sourcesView.showSourceLocation(uiSourceCode, lineNumber, columnNumber); 844 }, 845 846 /** 847 * @param {string} query 848 * @return {boolean} 849 */ 850 shouldShowMatchingItems: function(query) 851 { 852 return !query.startsWith(":"); 853 }, 854 855 /** 856 * @param {!WebInspector.Project} project 857 * @return {boolean} 858 */ 859 filterProject: function(project) 860 { 861 return !project.isServiceProject(); 862 }, 863 864 __proto__: WebInspector.SelectUISourceCodeDialog.prototype 865} 866 867/** 868 * @param {!WebInspector.SourcesView} sourcesView 869 * @param {!Element} relativeToElement 870 * @param {string=} query 871 * @param {!Map.<!WebInspector.UISourceCode, number>=} defaultScores 872 */ 873WebInspector.OpenResourceDialog.show = function(sourcesView, relativeToElement, query, defaultScores) 874{ 875 if (WebInspector.Dialog.currentInstance()) 876 return; 877 878 var filteredItemSelectionDialog = new WebInspector.FilteredItemSelectionDialog(new WebInspector.OpenResourceDialog(sourcesView, defaultScores)); 879 filteredItemSelectionDialog.renderAsTwoRows(); 880 if (query) 881 filteredItemSelectionDialog.setQuery(query); 882 WebInspector.Dialog.show(relativeToElement, filteredItemSelectionDialog); 883} 884 885/** 886 * @constructor 887 * @extends {WebInspector.SelectUISourceCodeDialog} 888 * @param {!Array.<string>} types 889 * @param {function(!WebInspector.UISourceCode)} callback 890 */ 891WebInspector.SelectUISourceCodeForProjectTypesDialog = function(types, callback) 892{ 893 this._types = types; 894 WebInspector.SelectUISourceCodeDialog.call(this); 895 this._callback = callback; 896} 897 898WebInspector.SelectUISourceCodeForProjectTypesDialog.prototype = { 899 /** 900 * @param {!WebInspector.UISourceCode} uiSourceCode 901 * @param {number=} lineNumber 902 * @param {number=} columnNumber 903 */ 904 uiSourceCodeSelected: function(uiSourceCode, lineNumber, columnNumber) 905 { 906 this._callback(uiSourceCode); 907 }, 908 909 /** 910 * @param {!WebInspector.Project} project 911 * @return {boolean} 912 */ 913 filterProject: function(project) 914 { 915 return this._types.indexOf(project.type()) !== -1; 916 }, 917 918 __proto__: WebInspector.SelectUISourceCodeDialog.prototype 919} 920 921/** 922 * @param {string} name 923 * @param {!Array.<string>} types 924 * @param {function(!WebInspector.UISourceCode)} callback 925 * @param {!Element} relativeToElement 926 */ 927WebInspector.SelectUISourceCodeForProjectTypesDialog.show = function(name, types, callback, relativeToElement) 928{ 929 if (WebInspector.Dialog.currentInstance()) 930 return; 931 932 var filteredItemSelectionDialog = new WebInspector.FilteredItemSelectionDialog(new WebInspector.SelectUISourceCodeForProjectTypesDialog(types, callback)); 933 filteredItemSelectionDialog.setQuery(name); 934 filteredItemSelectionDialog.renderAsTwoRows(); 935 WebInspector.Dialog.show(relativeToElement, filteredItemSelectionDialog); 936} 937 938/** 939 * @typedef {{index: number, total: number, chunk: !Array.<!{selectorText: string, lineNumber: number, columnNumber: number}>}} 940 */ 941WebInspector.JavaScriptOutlineDialog.MessageEventData; 942