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._itemElementsContainer = this._viewportControl.element; 58 this._itemElementsContainer.classList.add("container"); 59 this._itemElementsContainer.classList.add("monospace"); 60 this._itemElementsContainer.addEventListener("click", this._onClick.bind(this), false); 61 this.element.appendChild(this._itemElementsContainer); 62 63 this._delegate = delegate; 64 this._delegate.setRefreshCallback(this._itemsLoaded.bind(this)); 65 this._itemsLoaded(); 66} 67 68WebInspector.FilteredItemSelectionDialog.prototype = { 69 /** 70 * @param {!Element} element 71 * @param {!Element} relativeToElement 72 */ 73 position: function(element, relativeToElement) 74 { 75 const shadow = 10; 76 const shadowPadding = 20; // shadow + padding 77 var container = WebInspector.Dialog.modalHostView().element; 78 var preferredWidth = Math.max(relativeToElement.offsetWidth * 2 / 3, 500); 79 var width = Math.min(preferredWidth, container.offsetWidth - 2 * shadowPadding); 80 var preferredHeight = Math.max(relativeToElement.offsetHeight * 2 / 3, 204); 81 var height = Math.min(preferredHeight, container.offsetHeight - 2 * shadowPadding); 82 83 this.element.style.width = width + "px"; 84 var box = relativeToElement.boxInWindow(window).relativeToElement(container); 85 var positionX = box.x + Math.max((box.width - width - 2 * shadowPadding) / 2, shadow); 86 positionX = Math.max(shadow, Math.min(container.offsetWidth - width - 2 * shadowPadding, positionX)); 87 var positionY = box.y + Math.max((box.height - height - 2 * shadowPadding) / 2, shadow); 88 positionY = Math.max(shadow, Math.min(container.offsetHeight - height - 2 * shadowPadding, positionY)); 89 element.positionAt(positionX, positionY, container); 90 this._dialogHeight = height; 91 92 this._updateShowMatchingItems(); 93 this._viewportControl.refresh(); 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 if (!this._filteredItems.length) 329 return; 330 var element = this._viewportControl.renderedElementAt(this._selectedIndexInFiltered); 331 if (element) 332 element.classList.remove("selected"); 333 this._viewportControl.scrollItemIntoView(index, makeLast); 334 this._selectedIndexInFiltered = index; 335 element = this._viewportControl.renderedElementAt(index); 336 if (element) 337 element.classList.add("selected"); 338 }, 339 340 _onClick: function(event) 341 { 342 var itemElement = event.target.enclosingNodeOrSelfWithClass("filtered-item-list-dialog-item"); 343 if (!itemElement) 344 return; 345 this._delegate.selectItem(itemElement._index, this._promptElement.value.trim()); 346 WebInspector.Dialog.hide(); 347 }, 348 349 /** 350 * @return {number} 351 */ 352 itemCount: function() 353 { 354 return this._filteredItems.length; 355 }, 356 357 /** 358 * @param {number} index 359 * @return {number} 360 */ 361 fastHeight: function(index) 362 { 363 if (!this._rowHeight) { 364 var delegateIndex = this._filteredItems[index]; 365 var element = this._createItemElement(delegateIndex); 366 this._rowHeight = element.measurePreferredSize(this._viewportControl.contentElement()).height; 367 } 368 return this._rowHeight; 369 }, 370 371 /** 372 * @param {number} index 373 * @return {!WebInspector.ViewportElement} 374 */ 375 itemElement: function(index) 376 { 377 var delegateIndex = this._filteredItems[index]; 378 var element = this._createItemElement(delegateIndex); 379 if (index === this._selectedIndexInFiltered) 380 element.classList.add("selected"); 381 return new WebInspector.StaticViewportElement(element); 382 }, 383 384 /** 385 * @return {number} 386 */ 387 minimumRowHeight: function() 388 { 389 return this.fastHeight(0); 390 }, 391 392 __proto__: WebInspector.DialogDelegate.prototype 393} 394 395/** 396 * @constructor 397 */ 398WebInspector.SelectionDialogContentProvider = function() 399{ 400} 401 402WebInspector.SelectionDialogContentProvider.prototype = { 403 /** 404 * @param {function():void} refreshCallback 405 */ 406 setRefreshCallback: function(refreshCallback) 407 { 408 this._refreshCallback = refreshCallback; 409 }, 410 411 /** 412 * @param {string} query 413 * @return {boolean} 414 */ 415 shouldShowMatchingItems: function(query) 416 { 417 return true; 418 }, 419 420 /** 421 * @return {number} 422 */ 423 itemCount: function() 424 { 425 return 0; 426 }, 427 428 /** 429 * @param {number} itemIndex 430 * @return {string} 431 */ 432 itemKeyAt: function(itemIndex) 433 { 434 return ""; 435 }, 436 437 /** 438 * @param {number} itemIndex 439 * @param {string} query 440 * @return {number} 441 */ 442 itemScoreAt: function(itemIndex, query) 443 { 444 return 1; 445 }, 446 447 /** 448 * @param {number} itemIndex 449 * @param {string} query 450 * @param {!Element} titleElement 451 * @param {!Element} subtitleElement 452 */ 453 renderItem: function(itemIndex, query, titleElement, subtitleElement) 454 { 455 }, 456 457 /** 458 * @param {!Element} element 459 * @param {string} query 460 * @return {boolean} 461 */ 462 highlightRanges: function(element, query) 463 { 464 if (!query) 465 return false; 466 467 /** 468 * @param {string} text 469 * @param {string} query 470 * @return {?Array.<!WebInspector.SourceRange>} 471 */ 472 function rangesForMatch(text, query) 473 { 474 var sm = new difflib.SequenceMatcher(query, text); 475 var opcodes = sm.get_opcodes(); 476 var ranges = []; 477 478 for (var i = 0; i < opcodes.length; ++i) { 479 var opcode = opcodes[i]; 480 if (opcode[0] === "equal") 481 ranges.push(new WebInspector.SourceRange(opcode[3], opcode[4] - opcode[3])); 482 else if (opcode[0] !== "insert") 483 return null; 484 } 485 return ranges; 486 } 487 488 var text = element.textContent; 489 var ranges = rangesForMatch(text, query); 490 if (!ranges) 491 ranges = rangesForMatch(text.toUpperCase(), query.toUpperCase()); 492 if (ranges) { 493 WebInspector.highlightRangesWithStyleClass(element, ranges, "highlight"); 494 return true; 495 } 496 return false; 497 }, 498 499 /** 500 * @param {?number} itemIndex 501 * @param {string} promptValue 502 */ 503 selectItem: function(itemIndex, promptValue) 504 { 505 }, 506 507 refresh: function() 508 { 509 this._refreshCallback(); 510 }, 511 512 /** 513 * @param {string} query 514 * @return {string} 515 */ 516 rewriteQuery: function(query) 517 { 518 return query; 519 }, 520 521 dispose: function() 522 { 523 } 524} 525 526/** 527 * @constructor 528 * @extends {WebInspector.SelectionDialogContentProvider} 529 * @param {!WebInspector.UISourceCode} uiSourceCode 530 * @param {function(number, number)} selectItemCallback 531 */ 532WebInspector.JavaScriptOutlineDialog = function(uiSourceCode, selectItemCallback) 533{ 534 WebInspector.SelectionDialogContentProvider.call(this); 535 536 this._functionItems = []; 537 this._selectItemCallback = selectItemCallback; 538 this._outlineWorker = Runtime.startWorker("script_formatter_worker"); 539 this._outlineWorker.onmessage = this._didBuildOutlineChunk.bind(this); 540 this._outlineWorker.postMessage({ method: "javaScriptOutline", params: { content: uiSourceCode.workingCopy() } }); 541} 542 543/** 544 * @param {!WebInspector.View} view 545 * @param {!WebInspector.UISourceCode} uiSourceCode 546 * @param {function(number, number)} selectItemCallback 547 */ 548WebInspector.JavaScriptOutlineDialog.show = function(view, uiSourceCode, selectItemCallback) 549{ 550 if (WebInspector.Dialog.currentInstance()) 551 return; 552 var filteredItemSelectionDialog = new WebInspector.FilteredItemSelectionDialog(new WebInspector.JavaScriptOutlineDialog(uiSourceCode, selectItemCallback)); 553 WebInspector.Dialog.show(view.element, filteredItemSelectionDialog); 554} 555 556WebInspector.JavaScriptOutlineDialog.prototype = { 557 /** 558 * @param {!MessageEvent} event 559 */ 560 _didBuildOutlineChunk: function(event) 561 { 562 var data = /** @type {!WebInspector.JavaScriptOutlineDialog.MessageEventData} */ (event.data); 563 var chunk = data.chunk; 564 for (var i = 0; i < chunk.length; ++i) 565 this._functionItems.push(chunk[i]); 566 567 if (data.total === data.index + 1) 568 this.dispose(); 569 570 this.refresh(); 571 }, 572 573 /** 574 * @return {number} 575 */ 576 itemCount: function() 577 { 578 return this._functionItems.length; 579 }, 580 581 /** 582 * @param {number} itemIndex 583 * @return {string} 584 */ 585 itemKeyAt: function(itemIndex) 586 { 587 return this._functionItems[itemIndex].name; 588 }, 589 590 /** 591 * @param {number} itemIndex 592 * @param {string} query 593 * @return {number} 594 */ 595 itemScoreAt: function(itemIndex, query) 596 { 597 var item = this._functionItems[itemIndex]; 598 return -item.line; 599 }, 600 601 /** 602 * @param {number} itemIndex 603 * @param {string} query 604 * @param {!Element} titleElement 605 * @param {!Element} subtitleElement 606 */ 607 renderItem: function(itemIndex, query, titleElement, subtitleElement) 608 { 609 var item = this._functionItems[itemIndex]; 610 titleElement.textContent = item.name + (item.arguments ? item.arguments : ""); 611 this.highlightRanges(titleElement, query); 612 subtitleElement.textContent = ":" + (item.line + 1); 613 }, 614 615 /** 616 * @param {?number} itemIndex 617 * @param {string} promptValue 618 */ 619 selectItem: function(itemIndex, promptValue) 620 { 621 if (itemIndex === null) 622 return; 623 var lineNumber = this._functionItems[itemIndex].line; 624 if (!isNaN(lineNumber) && lineNumber >= 0) 625 this._selectItemCallback(lineNumber, this._functionItems[itemIndex].column); 626 }, 627 628 dispose: function() 629 { 630 if (this._outlineWorker) { 631 this._outlineWorker.terminate(); 632 delete this._outlineWorker; 633 } 634 }, 635 636 __proto__: WebInspector.SelectionDialogContentProvider.prototype 637} 638 639/** 640 * @constructor 641 * @extends {WebInspector.SelectionDialogContentProvider} 642 * @param {!Map.<!WebInspector.UISourceCode, number>=} defaultScores 643 */ 644WebInspector.SelectUISourceCodeDialog = function(defaultScores) 645{ 646 WebInspector.SelectionDialogContentProvider.call(this); 647 648 this._populate(); 649 this._defaultScores = defaultScores; 650 this._scorer = new WebInspector.FilePathScoreFunction(""); 651 WebInspector.workspace.addEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this); 652 WebInspector.workspace.addEventListener(WebInspector.Workspace.Events.ProjectRemoved, this._projectRemoved, this); 653} 654 655WebInspector.SelectUISourceCodeDialog.prototype = { 656 _projectRemoved: function(event) 657 { 658 var project = /** @type {!WebInspector.Project} */ (event.data); 659 this._populate(project); 660 this.refresh(); 661 }, 662 663 /** 664 * @param {!WebInspector.Project=} skipProject 665 */ 666 _populate: function(skipProject) 667 { 668 /** @type {!Array.<!WebInspector.UISourceCode>} */ 669 this._uiSourceCodes = []; 670 var projects = WebInspector.workspace.projects().filter(this.filterProject.bind(this)); 671 for (var i = 0; i < projects.length; ++i) { 672 if (skipProject && projects[i] === skipProject) 673 continue; 674 this._uiSourceCodes = this._uiSourceCodes.concat(projects[i].uiSourceCodes()); 675 } 676 }, 677 678 /** 679 * @param {?WebInspector.UISourceCode} uiSourceCode 680 * @param {number=} lineNumber 681 * @param {number=} columnNumber 682 */ 683 uiSourceCodeSelected: function(uiSourceCode, lineNumber, columnNumber) 684 { 685 // Overridden by subclasses 686 }, 687 688 /** 689 * @param {!WebInspector.Project} project 690 * @return {boolean} 691 */ 692 filterProject: function(project) 693 { 694 return true; 695 // Overridden by subclasses 696 }, 697 698 /** 699 * @return {number} 700 */ 701 itemCount: function() 702 { 703 return this._uiSourceCodes.length; 704 }, 705 706 /** 707 * @param {number} itemIndex 708 * @return {string} 709 */ 710 itemKeyAt: function(itemIndex) 711 { 712 return this._uiSourceCodes[itemIndex].fullDisplayName(); 713 }, 714 715 /** 716 * @param {number} itemIndex 717 * @param {string} query 718 * @return {number} 719 */ 720 itemScoreAt: function(itemIndex, query) 721 { 722 var uiSourceCode = this._uiSourceCodes[itemIndex]; 723 var score = this._defaultScores ? (this._defaultScores.get(uiSourceCode) || 0) : 0; 724 if (!query || query.length < 2) 725 return score; 726 727 if (this._query !== query) { 728 this._query = query; 729 this._scorer = new WebInspector.FilePathScoreFunction(query); 730 } 731 732 var path = uiSourceCode.fullDisplayName(); 733 return score + 10 * this._scorer.score(path, null); 734 }, 735 736 /** 737 * @param {number} itemIndex 738 * @param {string} query 739 * @param {!Element} titleElement 740 * @param {!Element} subtitleElement 741 * @return {!Array.<!Element>} 742 */ 743 renderItem: function(itemIndex, query, titleElement, subtitleElement) 744 { 745 query = this.rewriteQuery(query); 746 var uiSourceCode = this._uiSourceCodes[itemIndex]; 747 titleElement.textContent = uiSourceCode.displayName() + (this._queryLineNumberAndColumnNumber || ""); 748 subtitleElement.textContent = uiSourceCode.fullDisplayName().trimEnd(100); 749 750 var indexes = []; 751 var score = new WebInspector.FilePathScoreFunction(query).score(subtitleElement.textContent, indexes); 752 var fileNameIndex = subtitleElement.textContent.lastIndexOf("/"); 753 var ranges = []; 754 for (var i = 0; i < indexes.length; ++i) 755 ranges.push({offset: indexes[i], length: 1}); 756 if (indexes[0] > fileNameIndex) { 757 for (var i = 0; i < ranges.length; ++i) 758 ranges[i].offset -= fileNameIndex + 1; 759 return WebInspector.highlightRangesWithStyleClass(titleElement, ranges, "highlight"); 760 } else { 761 return WebInspector.highlightRangesWithStyleClass(subtitleElement, ranges, "highlight"); 762 } 763 }, 764 765 /** 766 * @param {?number} itemIndex 767 * @param {string} promptValue 768 */ 769 selectItem: function(itemIndex, promptValue) 770 { 771 var parsedExpression = promptValue.trim().match(/^([^:]*)(:\d+)?(:\d+)?$/); 772 if (!parsedExpression) 773 return; 774 775 var lineNumber; 776 var columnNumber; 777 if (parsedExpression[2]) 778 lineNumber = parseInt(parsedExpression[2].substr(1), 10) - 1; 779 if (parsedExpression[3]) 780 columnNumber = parseInt(parsedExpression[3].substr(1), 10) - 1; 781 var uiSourceCode = itemIndex !== null ? this._uiSourceCodes[itemIndex] : null; 782 this.uiSourceCodeSelected(uiSourceCode, lineNumber, columnNumber); 783 }, 784 785 /** 786 * @param {string} query 787 * @return {string} 788 */ 789 rewriteQuery: function(query) 790 { 791 if (!query) 792 return query; 793 query = query.trim(); 794 var lineNumberMatch = query.match(/^([^:]+)((?::[^:]*){0,2})$/); 795 this._queryLineNumberAndColumnNumber = lineNumberMatch ? lineNumberMatch[2] : ""; 796 return lineNumberMatch ? lineNumberMatch[1] : query; 797 }, 798 799 /** 800 * @param {!WebInspector.Event} event 801 */ 802 _uiSourceCodeAdded: function(event) 803 { 804 var uiSourceCode = /** @type {!WebInspector.UISourceCode} */ (event.data); 805 if (!this.filterProject(uiSourceCode.project())) 806 return; 807 this._uiSourceCodes.push(uiSourceCode) 808 this.refresh(); 809 }, 810 811 dispose: function() 812 { 813 WebInspector.workspace.removeEventListener(WebInspector.Workspace.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this); 814 WebInspector.workspace.removeEventListener(WebInspector.Workspace.Events.ProjectRemoved, this._projectRemoved, this); 815 }, 816 817 __proto__: WebInspector.SelectionDialogContentProvider.prototype 818} 819 820/** 821 * @constructor 822 * @extends {WebInspector.SelectUISourceCodeDialog} 823 * @param {!WebInspector.SourcesView} sourcesView 824 * @param {!Map.<!WebInspector.UISourceCode, number>=} defaultScores 825 */ 826WebInspector.OpenResourceDialog = function(sourcesView, defaultScores) 827{ 828 WebInspector.SelectUISourceCodeDialog.call(this, defaultScores); 829 this._sourcesView = sourcesView; 830} 831 832WebInspector.OpenResourceDialog.prototype = { 833 834 /** 835 * @param {?WebInspector.UISourceCode} uiSourceCode 836 * @param {number=} lineNumber 837 * @param {number=} columnNumber 838 */ 839 uiSourceCodeSelected: function(uiSourceCode, lineNumber, columnNumber) 840 { 841 if (!uiSourceCode) 842 uiSourceCode = this._sourcesView.currentUISourceCode(); 843 if (!uiSourceCode) 844 return; 845 this._sourcesView.showSourceLocation(uiSourceCode, lineNumber, columnNumber); 846 }, 847 848 /** 849 * @param {string} query 850 * @return {boolean} 851 */ 852 shouldShowMatchingItems: function(query) 853 { 854 return !query.startsWith(":"); 855 }, 856 857 /** 858 * @param {!WebInspector.Project} project 859 * @return {boolean} 860 */ 861 filterProject: function(project) 862 { 863 return !project.isServiceProject(); 864 }, 865 866 __proto__: WebInspector.SelectUISourceCodeDialog.prototype 867} 868 869/** 870 * @param {!WebInspector.SourcesView} sourcesView 871 * @param {!Element} relativeToElement 872 * @param {string=} query 873 * @param {!Map.<!WebInspector.UISourceCode, number>=} defaultScores 874 */ 875WebInspector.OpenResourceDialog.show = function(sourcesView, relativeToElement, query, defaultScores) 876{ 877 if (WebInspector.Dialog.currentInstance()) 878 return; 879 880 var filteredItemSelectionDialog = new WebInspector.FilteredItemSelectionDialog(new WebInspector.OpenResourceDialog(sourcesView, defaultScores)); 881 filteredItemSelectionDialog.renderAsTwoRows(); 882 WebInspector.Dialog.show(relativeToElement, filteredItemSelectionDialog); 883 if (query) 884 filteredItemSelectionDialog.setQuery(query); 885} 886 887/** 888 * @constructor 889 * @extends {WebInspector.SelectUISourceCodeDialog} 890 * @param {!Array.<string>} types 891 * @param {function(!WebInspector.UISourceCode)} callback 892 */ 893WebInspector.SelectUISourceCodeForProjectTypesDialog = function(types, callback) 894{ 895 this._types = types; 896 WebInspector.SelectUISourceCodeDialog.call(this); 897 this._callback = callback; 898} 899 900WebInspector.SelectUISourceCodeForProjectTypesDialog.prototype = { 901 /** 902 * @param {!WebInspector.UISourceCode} uiSourceCode 903 * @param {number=} lineNumber 904 * @param {number=} columnNumber 905 */ 906 uiSourceCodeSelected: function(uiSourceCode, lineNumber, columnNumber) 907 { 908 this._callback(uiSourceCode); 909 }, 910 911 /** 912 * @param {!WebInspector.Project} project 913 * @return {boolean} 914 */ 915 filterProject: function(project) 916 { 917 return this._types.indexOf(project.type()) !== -1; 918 }, 919 920 __proto__: WebInspector.SelectUISourceCodeDialog.prototype 921} 922 923/** 924 * @param {string} name 925 * @param {!Array.<string>} types 926 * @param {function(!WebInspector.UISourceCode)} callback 927 * @param {!Element} relativeToElement 928 */ 929WebInspector.SelectUISourceCodeForProjectTypesDialog.show = function(name, types, callback, relativeToElement) 930{ 931 if (WebInspector.Dialog.currentInstance()) 932 return; 933 934 var filteredItemSelectionDialog = new WebInspector.FilteredItemSelectionDialog(new WebInspector.SelectUISourceCodeForProjectTypesDialog(types, callback)); 935 filteredItemSelectionDialog.setQuery(name); 936 filteredItemSelectionDialog.renderAsTwoRows(); 937 WebInspector.Dialog.show(relativeToElement, filteredItemSelectionDialog); 938} 939 940/** 941 * @typedef {{index: number, total: number, chunk: !Array.<!{selectorText: string, lineNumber: number, columnNumber: number}>}} 942 */ 943WebInspector.JavaScriptOutlineDialog.MessageEventData; 944