mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-07 12:33:47 +08:00
This rewrites the following chrome-side test inspector-protocol/heap-profiler/heap-snapshot-merged-nodes.js as end-to-end test. The CL also introduces the necessary helpers to port the remaining tests to e2e tests. Bug: chromium:1110817 Change-Id: I5948a974d3300d7ae309793daaebec85f79d2f56 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2398821 Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Reviewed-by: Paul Lewis <aerotwist@chromium.org> Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
647 lines
19 KiB
JavaScript
647 lines
19 KiB
JavaScript
/*
|
|
* Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
|
|
* Copyright (C) 2007 Matt Lilek (pewtermoose@gmail.com).
|
|
* Copyright (C) 2009 Joseph Pecoraro
|
|
* Copyright (C) 2011 Google Inc. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
|
|
* its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
// @ts-nocheck
|
|
// TODO(crbug.com/1011811): Enable TypeScript compiler checks
|
|
|
|
import * as Common from '../common/common.js';
|
|
|
|
import * as ARIAUtils from './ARIAUtils.js';
|
|
import {HistoryInput} from './HistoryInput.js';
|
|
import {InspectorView} from './InspectorView.js';
|
|
import {Toolbar, ToolbarButton, ToolbarToggle} from './Toolbar.js';
|
|
import {createTextButton} from './UIUtils.js';
|
|
import {VBox} from './Widget.js';
|
|
|
|
/**
|
|
* @unrestricted
|
|
*/
|
|
export class SearchableView extends VBox {
|
|
/**
|
|
* @param {!Searchable} searchable
|
|
* @param {string=} settingName
|
|
*/
|
|
constructor(searchable, settingName) {
|
|
super(true);
|
|
this.registerRequiredCSS('ui/searchableView.css');
|
|
this.element[_symbol] = this;
|
|
|
|
this._searchProvider = searchable;
|
|
this._setting = settingName ? Common.Settings.Settings.instance().createSetting(settingName, {}) : null;
|
|
this._replaceable = false;
|
|
|
|
this.contentElement.createChild('slot');
|
|
this._footerElementContainer = this.contentElement.createChild('div', 'search-bar hidden');
|
|
this._footerElementContainer.style.order = 100;
|
|
this._footerElement = this._footerElementContainer.createChild('div', 'toolbar-search');
|
|
|
|
const replaceToggleToolbar = new Toolbar('replace-toggle-toolbar', this._footerElement);
|
|
this._replaceToggleButton = new ToolbarToggle(Common.UIString.UIString('Replace'), 'mediumicon-replace');
|
|
this._replaceToggleButton.addEventListener(ToolbarButton.Events.Click, this._toggleReplace, this);
|
|
replaceToggleToolbar.appendToolbarItem(this._replaceToggleButton);
|
|
|
|
const searchInputElements = this._footerElement.createChild('div', 'toolbar-search-inputs');
|
|
const searchControlElement = searchInputElements.createChild('div', 'toolbar-search-control');
|
|
|
|
this._searchInputElement = HistoryInput.create();
|
|
this._searchInputElement.type = 'search';
|
|
this._searchInputElement.classList.add('search-replace', 'custom-search-input');
|
|
this._searchInputElement.id = 'search-input-field';
|
|
this._searchInputElement.placeholder = Common.UIString.UIString('Find');
|
|
searchControlElement.appendChild(this._searchInputElement);
|
|
|
|
this._matchesElement = searchControlElement.createChild('label', 'search-results-matches');
|
|
this._matchesElement.setAttribute('for', 'search-input-field');
|
|
|
|
const searchNavigationElement = searchControlElement.createChild('div', 'toolbar-search-navigation-controls');
|
|
|
|
this._searchNavigationPrevElement =
|
|
searchNavigationElement.createChild('div', 'toolbar-search-navigation toolbar-search-navigation-prev');
|
|
this._searchNavigationPrevElement.addEventListener('click', this._onPrevButtonSearch.bind(this), false);
|
|
this._searchNavigationPrevElement.title = Common.UIString.UIString('Search previous');
|
|
ARIAUtils.setAccessibleName(this._searchNavigationPrevElement, Common.UIString.UIString('Search previous'));
|
|
|
|
this._searchNavigationNextElement =
|
|
searchNavigationElement.createChild('div', 'toolbar-search-navigation toolbar-search-navigation-next');
|
|
this._searchNavigationNextElement.addEventListener('click', this._onNextButtonSearch.bind(this), false);
|
|
this._searchNavigationNextElement.title = Common.UIString.UIString('Search next');
|
|
ARIAUtils.setAccessibleName(this._searchNavigationNextElement, Common.UIString.UIString('Search next'));
|
|
|
|
this._searchInputElement.addEventListener('keydown', this._onSearchKeyDown.bind(this), true);
|
|
this._searchInputElement.addEventListener('input', this._onInput.bind(this), false);
|
|
|
|
this._replaceInputElement =
|
|
searchInputElements.createChild('input', 'search-replace toolbar-replace-control hidden');
|
|
this._replaceInputElement.addEventListener('keydown', this._onReplaceKeyDown.bind(this), true);
|
|
this._replaceInputElement.placeholder = Common.UIString.UIString('Replace');
|
|
|
|
this._buttonsContainer = this._footerElement.createChild('div', 'toolbar-search-buttons');
|
|
const firstRowButtons = this._buttonsContainer.createChild('div', 'first-row-buttons');
|
|
|
|
const toolbar = new Toolbar('toolbar-search-options', firstRowButtons);
|
|
|
|
if (this._searchProvider.supportsCaseSensitiveSearch()) {
|
|
this._caseSensitiveButton = new ToolbarToggle(Common.UIString.UIString('Match Case'));
|
|
this._caseSensitiveButton.setText('Aa');
|
|
this._caseSensitiveButton.addEventListener(ToolbarButton.Events.Click, this._toggleCaseSensitiveSearch, this);
|
|
toolbar.appendToolbarItem(this._caseSensitiveButton);
|
|
}
|
|
|
|
if (this._searchProvider.supportsRegexSearch()) {
|
|
this._regexButton = new ToolbarToggle(Common.UIString.UIString('Use Regular Expression'));
|
|
this._regexButton.setText('.*');
|
|
this._regexButton.addEventListener(ToolbarButton.Events.Click, this._toggleRegexSearch, this);
|
|
toolbar.appendToolbarItem(this._regexButton);
|
|
}
|
|
|
|
const cancelButtonElement =
|
|
createTextButton(Common.UIString.UIString('Cancel'), this.closeSearch.bind(this), 'search-action-button');
|
|
firstRowButtons.appendChild(cancelButtonElement);
|
|
|
|
this._secondRowButtons = this._buttonsContainer.createChild('div', 'second-row-buttons hidden');
|
|
|
|
this._replaceButtonElement =
|
|
createTextButton(Common.UIString.UIString('Replace'), this._replace.bind(this), 'search-action-button');
|
|
this._replaceButtonElement.disabled = true;
|
|
this._secondRowButtons.appendChild(this._replaceButtonElement);
|
|
|
|
this._replaceAllButtonElement =
|
|
createTextButton(Common.UIString.UIString('Replace all'), this._replaceAll.bind(this), 'search-action-button');
|
|
this._secondRowButtons.appendChild(this._replaceAllButtonElement);
|
|
this._replaceAllButtonElement.disabled = true;
|
|
|
|
this._minimalSearchQuerySize = 3;
|
|
this._loadSetting();
|
|
}
|
|
|
|
/**
|
|
* @param {?Element} element
|
|
* @return {?SearchableView}
|
|
*/
|
|
static fromElement(element) {
|
|
let view = null;
|
|
while (element && !view) {
|
|
view = element[_symbol];
|
|
element = element.parentElementOrShadowHost();
|
|
}
|
|
return view;
|
|
}
|
|
|
|
_toggleCaseSensitiveSearch() {
|
|
this._caseSensitiveButton.setToggled(!this._caseSensitiveButton.toggled());
|
|
this._saveSetting();
|
|
this._performSearch(false, true);
|
|
}
|
|
|
|
_toggleRegexSearch() {
|
|
this._regexButton.setToggled(!this._regexButton.toggled());
|
|
this._saveSetting();
|
|
this._performSearch(false, true);
|
|
}
|
|
|
|
_toggleReplace() {
|
|
this._replaceToggleButton.setToggled(!this._replaceToggleButton.toggled());
|
|
this._updateSecondRowVisibility();
|
|
}
|
|
|
|
_saveSetting() {
|
|
if (!this._setting) {
|
|
return;
|
|
}
|
|
const settingValue = this._setting.get() || {};
|
|
settingValue.caseSensitive = this._caseSensitiveButton.toggled();
|
|
settingValue.isRegex = this._regexButton.toggled();
|
|
this._setting.set(settingValue);
|
|
}
|
|
|
|
_loadSetting() {
|
|
const settingValue = this._setting ? (this._setting.get() || {}) : {};
|
|
if (this._searchProvider.supportsCaseSensitiveSearch()) {
|
|
this._caseSensitiveButton.setToggled(!!settingValue.caseSensitive);
|
|
}
|
|
if (this._searchProvider.supportsRegexSearch()) {
|
|
this._regexButton.setToggled(!!settingValue.isRegex);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {number} minimalSearchQuerySize
|
|
*/
|
|
setMinimalSearchQuerySize(minimalSearchQuerySize) {
|
|
this._minimalSearchQuerySize = minimalSearchQuerySize;
|
|
}
|
|
|
|
/**
|
|
* @param {string} placeholder
|
|
* @param {string=} ariaLabel
|
|
*/
|
|
setPlaceholder(placeholder, ariaLabel) {
|
|
this._searchInputElement.placeholder = placeholder;
|
|
if (ariaLabel) {
|
|
ARIAUtils.setAccessibleName(this._searchInputElement, ariaLabel);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {boolean} replaceable
|
|
*/
|
|
setReplaceable(replaceable) {
|
|
this._replaceable = replaceable;
|
|
}
|
|
|
|
/**
|
|
* @param {number} matches
|
|
* @suppress {checkTypes}
|
|
*/
|
|
updateSearchMatchesCount(matches) {
|
|
if (this._searchProvider.currentSearchMatches === matches) {
|
|
return;
|
|
}
|
|
this._searchProvider.currentSearchMatches = matches;
|
|
this._updateSearchMatchesCountAndCurrentMatchIndex(this._searchProvider.currentQuery ? matches : 0, -1);
|
|
}
|
|
|
|
/**
|
|
* @param {number} currentMatchIndex
|
|
* @suppress {checkTypes}
|
|
*/
|
|
updateCurrentMatchIndex(currentMatchIndex) {
|
|
this._updateSearchMatchesCountAndCurrentMatchIndex(this._searchProvider.currentSearchMatches, currentMatchIndex);
|
|
}
|
|
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
isSearchVisible() {
|
|
return this._searchIsVisible;
|
|
}
|
|
|
|
closeSearch() {
|
|
this.cancelSearch();
|
|
if (this._footerElementContainer.hasFocus()) {
|
|
this.focus();
|
|
}
|
|
}
|
|
|
|
_toggleSearchBar(toggled) {
|
|
this._footerElementContainer.classList.toggle('hidden', !toggled);
|
|
this.doResize();
|
|
}
|
|
|
|
cancelSearch() {
|
|
if (!this._searchIsVisible) {
|
|
return;
|
|
}
|
|
this.resetSearch();
|
|
delete this._searchIsVisible;
|
|
this._toggleSearchBar(false);
|
|
}
|
|
|
|
resetSearch() {
|
|
this._clearSearch();
|
|
this._updateReplaceVisibility();
|
|
this._matchesElement.textContent = '';
|
|
}
|
|
|
|
refreshSearch() {
|
|
if (!this._searchIsVisible) {
|
|
return;
|
|
}
|
|
this.resetSearch();
|
|
this._performSearch(false, false);
|
|
}
|
|
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
handleFindNextShortcut() {
|
|
if (!this._searchIsVisible) {
|
|
return false;
|
|
}
|
|
this._searchProvider.jumpToNextSearchResult();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
handleFindPreviousShortcut() {
|
|
if (!this._searchIsVisible) {
|
|
return false;
|
|
}
|
|
this._searchProvider.jumpToPreviousSearchResult();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
handleFindShortcut() {
|
|
this.showSearchField();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
handleCancelSearchShortcut() {
|
|
if (!this._searchIsVisible) {
|
|
return false;
|
|
}
|
|
this.closeSearch();
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @param {boolean} enabled
|
|
*/
|
|
_updateSearchNavigationButtonState(enabled) {
|
|
this._replaceButtonElement.disabled = !enabled;
|
|
this._replaceAllButtonElement.disabled = !enabled;
|
|
this._searchNavigationPrevElement.classList.toggle('enabled', enabled);
|
|
this._searchNavigationNextElement.classList.toggle('enabled', enabled);
|
|
}
|
|
|
|
/**
|
|
* @param {number} matches
|
|
* @param {number} currentMatchIndex
|
|
*/
|
|
_updateSearchMatchesCountAndCurrentMatchIndex(matches, currentMatchIndex) {
|
|
if (!this._currentQuery) {
|
|
this._matchesElement.textContent = '';
|
|
} else if (matches === 0 || currentMatchIndex >= 0) {
|
|
this._matchesElement.textContent = Common.UIString.UIString('%d of %d', currentMatchIndex + 1, matches);
|
|
} else if (matches === 1) {
|
|
this._matchesElement.textContent = Common.UIString.UIString('1 match');
|
|
} else {
|
|
this._matchesElement.textContent = Common.UIString.UIString('%d matches', matches);
|
|
}
|
|
this._updateSearchNavigationButtonState(matches > 0);
|
|
}
|
|
|
|
showSearchField() {
|
|
if (this._searchIsVisible) {
|
|
this.cancelSearch();
|
|
}
|
|
|
|
let queryCandidate;
|
|
if (!this._searchInputElement.hasFocus()) {
|
|
const selection = InspectorView.instance().element.window().getSelection();
|
|
if (selection.rangeCount) {
|
|
queryCandidate = selection.toString().replace(/\r?\n.*/, '');
|
|
}
|
|
}
|
|
|
|
this._toggleSearchBar(true);
|
|
this._updateReplaceVisibility();
|
|
if (queryCandidate) {
|
|
this._searchInputElement.value = queryCandidate;
|
|
}
|
|
this._performSearch(false, false);
|
|
this._searchInputElement.focus();
|
|
this._searchInputElement.select();
|
|
this._searchIsVisible = true;
|
|
}
|
|
|
|
_updateReplaceVisibility() {
|
|
this._replaceToggleButton.setVisible(this._replaceable);
|
|
if (!this._replaceable) {
|
|
this._replaceToggleButton.setToggled(false);
|
|
this._updateSecondRowVisibility();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Event} event
|
|
*/
|
|
_onSearchKeyDown(event) {
|
|
if (isEscKey(event)) {
|
|
this.closeSearch();
|
|
event.consume(true);
|
|
return;
|
|
}
|
|
if (!isEnterKey(event)) {
|
|
return;
|
|
}
|
|
|
|
if (!this._currentQuery) {
|
|
this._performSearch(true, true, event.shiftKey);
|
|
} else {
|
|
this._jumpToNextSearchResult(event.shiftKey);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {!Event} event
|
|
*/
|
|
_onReplaceKeyDown(event) {
|
|
if (isEnterKey(event)) {
|
|
this._replace();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {boolean=} isBackwardSearch
|
|
*/
|
|
_jumpToNextSearchResult(isBackwardSearch) {
|
|
if (!this._currentQuery) {
|
|
return;
|
|
}
|
|
|
|
if (isBackwardSearch) {
|
|
this._searchProvider.jumpToPreviousSearchResult();
|
|
} else {
|
|
this._searchProvider.jumpToNextSearchResult();
|
|
}
|
|
}
|
|
|
|
_onNextButtonSearch(event) {
|
|
if (!this._searchNavigationNextElement.classList.contains('enabled')) {
|
|
return;
|
|
}
|
|
this._jumpToNextSearchResult();
|
|
this._searchInputElement.focus();
|
|
}
|
|
|
|
_onPrevButtonSearch(event) {
|
|
if (!this._searchNavigationPrevElement.classList.contains('enabled')) {
|
|
return;
|
|
}
|
|
this._jumpToNextSearchResult(true);
|
|
this._searchInputElement.focus();
|
|
}
|
|
|
|
_onFindClick(event) {
|
|
if (!this._currentQuery) {
|
|
this._performSearch(true, true);
|
|
} else {
|
|
this._jumpToNextSearchResult();
|
|
}
|
|
this._searchInputElement.focus();
|
|
}
|
|
|
|
_onPreviousClick(event) {
|
|
if (!this._currentQuery) {
|
|
this._performSearch(true, true, true);
|
|
} else {
|
|
this._jumpToNextSearchResult(true);
|
|
}
|
|
this._searchInputElement.focus();
|
|
}
|
|
|
|
/** @suppress {checkTypes} */
|
|
_clearSearch() {
|
|
delete this._currentQuery;
|
|
if (!!this._searchProvider.currentQuery) {
|
|
delete this._searchProvider.currentQuery;
|
|
this._searchProvider.searchCanceled();
|
|
}
|
|
this._updateSearchMatchesCountAndCurrentMatchIndex(0, -1);
|
|
}
|
|
|
|
/**
|
|
* @param {boolean} forceSearch
|
|
* @param {boolean} shouldJump
|
|
* @param {boolean=} jumpBackwards
|
|
* @suppress {checkTypes}
|
|
*/
|
|
_performSearch(forceSearch, shouldJump, jumpBackwards) {
|
|
const query = this._searchInputElement.value;
|
|
if (!query || (!forceSearch && query.length < this._minimalSearchQuerySize && !this._currentQuery)) {
|
|
this._clearSearch();
|
|
return;
|
|
}
|
|
|
|
this._currentQuery = query;
|
|
this._searchProvider.currentQuery = query;
|
|
|
|
const searchConfig = this._currentSearchConfig();
|
|
this._searchProvider.performSearch(searchConfig, shouldJump, jumpBackwards);
|
|
}
|
|
|
|
/**
|
|
* @return {!SearchConfig}
|
|
*/
|
|
_currentSearchConfig() {
|
|
const query = this._searchInputElement.value;
|
|
const caseSensitive = this._caseSensitiveButton ? this._caseSensitiveButton.toggled() : false;
|
|
const isRegex = this._regexButton ? this._regexButton.toggled() : false;
|
|
return new SearchConfig(query, caseSensitive, isRegex);
|
|
}
|
|
|
|
_updateSecondRowVisibility() {
|
|
const secondRowVisible = this._replaceToggleButton.toggled();
|
|
this._footerElementContainer.classList.toggle('replaceable', secondRowVisible);
|
|
this._secondRowButtons.classList.toggle('hidden', !secondRowVisible);
|
|
this._replaceInputElement.classList.toggle('hidden', !secondRowVisible);
|
|
|
|
if (secondRowVisible) {
|
|
this._replaceInputElement.focus();
|
|
} else {
|
|
this._searchInputElement.focus();
|
|
}
|
|
this.doResize();
|
|
}
|
|
|
|
_replace() {
|
|
const searchConfig = this._currentSearchConfig();
|
|
/** @type {!Replaceable} */ (this._searchProvider)
|
|
.replaceSelectionWith(searchConfig, this._replaceInputElement.value);
|
|
delete this._currentQuery;
|
|
this._performSearch(true, true);
|
|
}
|
|
|
|
_replaceAll() {
|
|
const searchConfig = this._currentSearchConfig();
|
|
/** @type {!Replaceable} */ (this._searchProvider).replaceAllWith(searchConfig, this._replaceInputElement.value);
|
|
}
|
|
|
|
/**
|
|
* @param {!Event} event
|
|
*/
|
|
_onInput(event) {
|
|
if (this._valueChangedTimeoutId) {
|
|
clearTimeout(this._valueChangedTimeoutId);
|
|
}
|
|
const timeout = this._searchInputElement.value.length < 3 ? 200 : 0;
|
|
this._valueChangedTimeoutId = setTimeout(this._onValueChanged.bind(this), timeout);
|
|
}
|
|
|
|
_onValueChanged() {
|
|
if (!this._searchIsVisible) {
|
|
return;
|
|
}
|
|
delete this._valueChangedTimeoutId;
|
|
this._performSearch(false, true);
|
|
}
|
|
}
|
|
|
|
export const _symbol = Symbol('searchableView');
|
|
|
|
|
|
/**
|
|
* @interface
|
|
*/
|
|
export class Searchable {
|
|
searchCanceled() {
|
|
}
|
|
|
|
/**
|
|
* @param {!SearchConfig} searchConfig
|
|
* @param {boolean} shouldJump
|
|
* @param {boolean=} jumpBackwards
|
|
*/
|
|
performSearch(searchConfig, shouldJump, jumpBackwards) {
|
|
}
|
|
|
|
jumpToNextSearchResult() {
|
|
}
|
|
|
|
jumpToPreviousSearchResult() {
|
|
}
|
|
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
supportsCaseSensitiveSearch() {
|
|
}
|
|
|
|
/**
|
|
* @return {boolean}
|
|
*/
|
|
supportsRegexSearch() {}
|
|
}
|
|
|
|
/**
|
|
* @interface
|
|
*/
|
|
export class Replaceable {
|
|
/**
|
|
* @param {!SearchConfig} searchConfig
|
|
* @param {string} replacement
|
|
*/
|
|
replaceSelectionWith(searchConfig, replacement) {
|
|
}
|
|
|
|
/**
|
|
* @param {!SearchConfig} searchConfig
|
|
* @param {string} replacement
|
|
*/
|
|
replaceAllWith(searchConfig, replacement) {}
|
|
}
|
|
|
|
/**
|
|
* @unrestricted
|
|
*/
|
|
export class SearchConfig {
|
|
/**
|
|
* @param {string} query
|
|
* @param {boolean} caseSensitive
|
|
* @param {boolean} isRegex
|
|
*/
|
|
constructor(query, caseSensitive, isRegex) {
|
|
this.query = query;
|
|
this.caseSensitive = caseSensitive;
|
|
this.isRegex = isRegex;
|
|
}
|
|
|
|
/**
|
|
* @param {boolean=} global
|
|
* @return {!RegExp}
|
|
*/
|
|
toSearchRegex(global) {
|
|
let modifiers = this.caseSensitive ? '' : 'i';
|
|
if (global) {
|
|
modifiers += 'g';
|
|
}
|
|
const query = this.isRegex ? '/' + this.query + '/' : this.query;
|
|
|
|
let regex;
|
|
|
|
// First try creating regex if user knows the / / hint.
|
|
try {
|
|
if (/^\/.+\/$/.test(query)) {
|
|
regex = new RegExp(query.substring(1, query.length - 1), modifiers);
|
|
regex.__fromRegExpQuery = true;
|
|
}
|
|
} catch (e) {
|
|
// Silent catch.
|
|
}
|
|
|
|
// Otherwise just do a plain text search.
|
|
if (!regex) {
|
|
regex = createPlainTextSearchRegex(query, modifiers);
|
|
}
|
|
|
|
return regex;
|
|
}
|
|
}
|