blob: 559a916f0da33907a4fdaebcb21c60d21a857cb9 [file] [log] [blame]
Blink Reformat4c46d092018-04-07 15:32:371// Copyright (c) 2015 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.
Tim van der Lippe97611c92020-02-12 16:56:584
5import * as Common from '../common/common.js';
Simon Zündda7058f2020-02-28 13:57:286import * as Platform from '../platform/platform.js';
Tim van der Lippe05149e972021-01-15 11:40:457import {ls} from '../platform/platform.js';
Tim van der Lippe97611c92020-02-12 16:56:588import * as SDK from '../sdk/sdk.js';
9import * as UI from '../ui/ui.js';
10
Tim van der Lippeaabc8302019-12-10 15:34:4511import {ElementsPanel} from './ElementsPanel.js';
12
Tim van der Lippe97611c92020-02-12 16:56:5813export class ClassesPaneWidget extends UI.Widget.Widget {
Blink Reformat4c46d092018-04-07 15:32:3714 constructor() {
15 super(true);
Jack Franklin71519f82020-11-03 12:08:5916 this.registerRequiredCSS('elements/classesPaneWidget.css', {enableLegacyPatching: true});
Blink Reformat4c46d092018-04-07 15:32:3717 this.contentElement.className = 'styles-element-classes-pane';
18 const container = this.contentElement.createChild('div', 'title-container');
19 this._input = container.createChild('div', 'new-class-input monospace');
20 this.setDefaultFocusedElement(this._input);
21 this._classesContainer = this.contentElement.createChild('div', 'source-code');
22 this._classesContainer.classList.add('styles-element-classes-container');
Tim van der Lippe13f71fb2019-11-29 11:17:3923 this._prompt = new ClassNamePrompt(this._nodeClasses.bind(this));
Blink Reformat4c46d092018-04-07 15:32:3724 this._prompt.setAutocompletionTimeout(0);
25 this._prompt.renderAsBlock();
26
Tim van der Lippebcd6b5c2021-01-13 12:31:5127 const proxyElement = /** @type {!HTMLElement} */ (this._prompt.attach(this._input));
Tim van der Lippe97611c92020-02-12 16:56:5828 this._prompt.setPlaceholder(Common.UIString.UIString('Add new class'));
Blink Reformat4c46d092018-04-07 15:32:3729 this._prompt.addEventListener(UI.TextPrompt.Events.TextChanged, this._onTextChanged, this);
30 proxyElement.addEventListener('keydown', this._onKeyDown.bind(this), false);
31
Paul Lewisdaac1062020-03-05 14:37:1032 SDK.SDKModel.TargetManager.instance().addModelListener(
Tim van der Lippe97611c92020-02-12 16:56:5833 SDK.DOMModel.DOMModel, SDK.DOMModel.Events.DOMMutated, this._onDOMMutated, this);
34 /** @type {!Set<!SDK.DOMModel.DOMNode>} */
Blink Reformat4c46d092018-04-07 15:32:3735 this._mutatingNodes = new Set();
Tim van der Lippe97611c92020-02-12 16:56:5836 /** @type {!Map<!SDK.DOMModel.DOMNode, string>} */
Blink Reformat4c46d092018-04-07 15:32:3737 this._pendingNodeClasses = new Map();
Tim van der Lippe97611c92020-02-12 16:56:5838 this._updateNodeThrottler = new Common.Throttler.Throttler(0);
39 /** @type {?SDK.DOMModel.DOMNode} */
Blink Reformat4c46d092018-04-07 15:32:3740 this._previousTarget = null;
Tim van der Lipped1a00aa2020-08-19 16:03:5641 UI.Context.Context.instance().addFlavorChangeListener(SDK.DOMModel.DOMNode, this._onSelectedNodeChanged, this);
Blink Reformat4c46d092018-04-07 15:32:3742 }
43
44 /**
45 * @param {string} text
46 * @return {!Array.<string>}
47 */
48 _splitTextIntoClasses(text) {
Mathias Bynens3abc0952020-04-20 14:15:5249 return text.split(/[,\s]/).map(className => className.trim()).filter(className => className.length);
Blink Reformat4c46d092018-04-07 15:32:3750 }
51
52 /**
Tim van der Lippebcd6b5c2021-01-13 12:31:5153 * @param {!KeyboardEvent} event
Blink Reformat4c46d092018-04-07 15:32:3754 */
55 _onKeyDown(event) {
Tim van der Lippebcd6b5c2021-01-13 12:31:5156 if (!(event.key === 'Enter') && !isEscKey(event)) {
Blink Reformat4c46d092018-04-07 15:32:3757 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:3458 }
Blink Reformat4c46d092018-04-07 15:32:3759
Tim van der Lippebcd6b5c2021-01-13 12:31:5160 if (event.key === 'Enter') {
Blink Reformat4c46d092018-04-07 15:32:3761 event.consume();
Tim van der Lippe1d6e57a2019-09-30 11:55:3462 if (this._prompt.acceptAutoComplete()) {
Blink Reformat4c46d092018-04-07 15:32:3763 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:3464 }
Blink Reformat4c46d092018-04-07 15:32:3765 }
66
Tim van der Lippe32f760f2020-10-01 10:52:1567 const eventTarget = /** @type {!HTMLElement} */ (event.target);
68 let text = /** @type {string} */ (eventTarget.textContent);
Blink Reformat4c46d092018-04-07 15:32:3769 if (isEscKey(event)) {
Simon Zündda7058f2020-02-28 13:57:2870 if (!Platform.StringUtilities.isWhitespace(text)) {
Blink Reformat4c46d092018-04-07 15:32:3771 event.consume(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:3472 }
Blink Reformat4c46d092018-04-07 15:32:3773 text = '';
74 }
75
76 this._prompt.clearAutocomplete();
Tim van der Lippe32f760f2020-10-01 10:52:1577 eventTarget.textContent = '';
Blink Reformat4c46d092018-04-07 15:32:3778
Tim van der Lipped1a00aa2020-08-19 16:03:5679 const node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
Tim van der Lippe1d6e57a2019-09-30 11:55:3480 if (!node) {
Blink Reformat4c46d092018-04-07 15:32:3781 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:3482 }
Blink Reformat4c46d092018-04-07 15:32:3783
84 const classNames = this._splitTextIntoClasses(text);
Michael Liaocafccfd2020-04-02 17:11:5985 if (!classNames.length) {
Patrick Brosset78fa1f52020-08-17 14:33:4886 this._installNodeClasses(node);
Michael Liaocafccfd2020-04-02 17:11:5987 return;
88 }
89
Tim van der Lippe1d6e57a2019-09-30 11:55:3490 for (const className of classNames) {
Blink Reformat4c46d092018-04-07 15:32:3791 this._toggleClass(node, className, true);
Tim van der Lippe1d6e57a2019-09-30 11:55:3492 }
Michael Liaocafccfd2020-04-02 17:11:5993
94 // annoucementString is used for screen reader to announce that the class(es) has been added successfully.
95 const joinClassString = classNames.join(' ');
96 const announcementString =
97 classNames.length > 1 ? ls`Classes ${joinClassString} added.` : ls`Class ${joinClassString} added.`;
98 UI.ARIAUtils.alert(announcementString, this.contentElement);
99
Blink Reformat4c46d092018-04-07 15:32:37100 this._installNodeClasses(node);
101 this._update();
102 }
103
104 _onTextChanged() {
Tim van der Lipped1a00aa2020-08-19 16:03:56105 const node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34106 if (!node) {
Blink Reformat4c46d092018-04-07 15:32:37107 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34108 }
Blink Reformat4c46d092018-04-07 15:32:37109 this._installNodeClasses(node);
110 }
111
112 /**
Tim van der Lippec02a97c2020-02-14 14:39:27113 * @param {!Common.EventTarget.EventTargetEvent} event
Blink Reformat4c46d092018-04-07 15:32:37114 */
115 _onDOMMutated(event) {
Tim van der Lippe97611c92020-02-12 16:56:58116 const node = /** @type {!SDK.DOMModel.DOMNode} */ (event.data);
Tim van der Lippe1d6e57a2019-09-30 11:55:34117 if (this._mutatingNodes.has(node)) {
Blink Reformat4c46d092018-04-07 15:32:37118 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34119 }
Tim van der Lippe32f760f2020-10-01 10:52:15120 cachedClassesMap.delete(node);
Blink Reformat4c46d092018-04-07 15:32:37121 this._update();
122 }
123
124 /**
Tim van der Lippec02a97c2020-02-14 14:39:27125 * @param {!Common.EventTarget.EventTargetEvent} event
Blink Reformat4c46d092018-04-07 15:32:37126 */
127 _onSelectedNodeChanged(event) {
128 if (this._previousTarget && this._prompt.text()) {
129 this._input.textContent = '';
130 this._installNodeClasses(this._previousTarget);
131 }
Tim van der Lippe97611c92020-02-12 16:56:58132 this._previousTarget = /** @type {?SDK.DOMModel.DOMNode} */ (event.data);
Blink Reformat4c46d092018-04-07 15:32:37133 this._update();
134 }
135
136 /**
137 * @override
138 */
139 wasShown() {
140 this._update();
141 }
142
143 _update() {
Tim van der Lippe1d6e57a2019-09-30 11:55:34144 if (!this.isShowing()) {
Blink Reformat4c46d092018-04-07 15:32:37145 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34146 }
Blink Reformat4c46d092018-04-07 15:32:37147
Tim van der Lipped1a00aa2020-08-19 16:03:56148 let node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34149 if (node) {
Blink Reformat4c46d092018-04-07 15:32:37150 node = node.enclosingElementOrSelf();
Tim van der Lippe1d6e57a2019-09-30 11:55:34151 }
Blink Reformat4c46d092018-04-07 15:32:37152
153 this._classesContainer.removeChildren();
Tim van der Lippe32f760f2020-10-01 10:52:15154 // @ts-ignore this._input is a div, not an input element. So this line makes no sense at all
Blink Reformat4c46d092018-04-07 15:32:37155 this._input.disabled = !node;
156
Tim van der Lippe1d6e57a2019-09-30 11:55:34157 if (!node) {
Blink Reformat4c46d092018-04-07 15:32:37158 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34159 }
Blink Reformat4c46d092018-04-07 15:32:37160
161 const classes = this._nodeClasses(node);
Simon Zündf27be3d2020-02-11 14:46:27162 const keys = [...classes.keys()];
Tim van der Lippe32f760f2020-10-01 10:52:15163 keys.sort(Platform.StringUtilities.caseInsensetiveComparator);
164 for (const className of keys) {
Tim van der Lippe97611c92020-02-12 16:56:58165 const label = UI.UIUtils.CheckboxLabel.create(className, classes.get(className));
Blink Reformat4c46d092018-04-07 15:32:37166 label.classList.add('monospace');
167 label.checkboxElement.addEventListener('click', this._onClick.bind(this, className), false);
168 this._classesContainer.appendChild(label);
169 }
170 }
171
172 /**
173 * @param {string} className
174 * @param {!Event} event
175 */
176 _onClick(className, event) {
Tim van der Lipped1a00aa2020-08-19 16:03:56177 const node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34178 if (!node) {
Blink Reformat4c46d092018-04-07 15:32:37179 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34180 }
Tim van der Lippe32f760f2020-10-01 10:52:15181 const enabled = /** @type {!HTMLInputElement} */ (event.target).checked;
Blink Reformat4c46d092018-04-07 15:32:37182 this._toggleClass(node, className, enabled);
183 this._installNodeClasses(node);
184 }
185
186 /**
Tim van der Lippe97611c92020-02-12 16:56:58187 * @param {!SDK.DOMModel.DOMNode} node
Blink Reformat4c46d092018-04-07 15:32:37188 * @return {!Map<string, boolean>}
189 */
190 _nodeClasses(node) {
Tim van der Lippe32f760f2020-10-01 10:52:15191 let result = cachedClassesMap.get(node);
Blink Reformat4c46d092018-04-07 15:32:37192 if (!result) {
193 const classAttribute = node.getAttribute('class') || '';
194 const classes = classAttribute.split(/\s/);
195 result = new Map();
196 for (let i = 0; i < classes.length; ++i) {
197 const className = classes[i].trim();
Tim van der Lippe1d6e57a2019-09-30 11:55:34198 if (!className.length) {
Blink Reformat4c46d092018-04-07 15:32:37199 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34200 }
Blink Reformat4c46d092018-04-07 15:32:37201 result.set(className, true);
202 }
Tim van der Lippe32f760f2020-10-01 10:52:15203 cachedClassesMap.set(node, result);
Blink Reformat4c46d092018-04-07 15:32:37204 }
205 return result;
206 }
207
208 /**
Tim van der Lippe97611c92020-02-12 16:56:58209 * @param {!SDK.DOMModel.DOMNode} node
Blink Reformat4c46d092018-04-07 15:32:37210 * @param {string} className
211 * @param {boolean} enabled
212 */
213 _toggleClass(node, className, enabled) {
214 const classes = this._nodeClasses(node);
215 classes.set(className, enabled);
216 }
217
218 /**
Tim van der Lippe97611c92020-02-12 16:56:58219 * @param {!SDK.DOMModel.DOMNode} node
Blink Reformat4c46d092018-04-07 15:32:37220 */
221 _installNodeClasses(node) {
222 const classes = this._nodeClasses(node);
223 const activeClasses = new Set();
224 for (const className of classes.keys()) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34225 if (classes.get(className)) {
Blink Reformat4c46d092018-04-07 15:32:37226 activeClasses.add(className);
Tim van der Lippe1d6e57a2019-09-30 11:55:34227 }
Blink Reformat4c46d092018-04-07 15:32:37228 }
229
230 const additionalClasses = this._splitTextIntoClasses(this._prompt.textWithCurrentSuggestion());
Tim van der Lippe1d6e57a2019-09-30 11:55:34231 for (const className of additionalClasses) {
Blink Reformat4c46d092018-04-07 15:32:37232 activeClasses.add(className);
Tim van der Lippe1d6e57a2019-09-30 11:55:34233 }
Blink Reformat4c46d092018-04-07 15:32:37234
Simon Zünda0d40622020-02-12 13:16:42235 const newClasses = [...activeClasses.values()].sort();
Blink Reformat4c46d092018-04-07 15:32:37236
237 this._pendingNodeClasses.set(node, newClasses.join(' '));
238 this._updateNodeThrottler.schedule(this._flushPendingClasses.bind(this));
239 }
240
241 /**
Tim van der Lippe32f760f2020-10-01 10:52:15242 * @return {!Promise<void>}
Blink Reformat4c46d092018-04-07 15:32:37243 */
Tim van der Lippe32f760f2020-10-01 10:52:15244 async _flushPendingClasses() {
Blink Reformat4c46d092018-04-07 15:32:37245 const promises = [];
246 for (const node of this._pendingNodeClasses.keys()) {
247 this._mutatingNodes.add(node);
Tim van der Lippe32f760f2020-10-01 10:52:15248 const promise = node.setAttributeValuePromise('class', /** @type {string} */ (this._pendingNodeClasses.get(node)))
Blink Reformat4c46d092018-04-07 15:32:37249 .then(onClassValueUpdated.bind(this, node));
250 promises.push(promise);
251 }
252 this._pendingNodeClasses.clear();
Tim van der Lippe32f760f2020-10-01 10:52:15253 await Promise.all(promises);
Blink Reformat4c46d092018-04-07 15:32:37254
255 /**
Tim van der Lippe97611c92020-02-12 16:56:58256 * @param {!SDK.DOMModel.DOMNode} node
Tim van der Lippe13f71fb2019-11-29 11:17:39257 * @this {ClassesPaneWidget}
Blink Reformat4c46d092018-04-07 15:32:37258 */
259 function onClassValueUpdated(node) {
260 this._mutatingNodes.delete(node);
261 }
262 }
Tim van der Lippe13f71fb2019-11-29 11:17:39263}
Blink Reformat4c46d092018-04-07 15:32:37264
Tim van der Lippe32f760f2020-10-01 10:52:15265/** @type {!WeakMap<!SDK.DOMModel.DOMNode, !Map<string, boolean>>} */
266const cachedClassesMap = new WeakMap();
Blink Reformat4c46d092018-04-07 15:32:37267
Andres Olivares43c2d9f2021-02-10 16:48:39268/** @type {!ButtonProvider} */
269let buttonProviderInstance;
270
Blink Reformat4c46d092018-04-07 15:32:37271/**
Tim van der Lippe97611c92020-02-12 16:56:58272 * @implements {UI.Toolbar.Provider}
Blink Reformat4c46d092018-04-07 15:32:37273 */
Tim van der Lippe13f71fb2019-11-29 11:17:39274export class ButtonProvider {
Andres Olivares43c2d9f2021-02-10 16:48:39275 /** @private */
Blink Reformat4c46d092018-04-07 15:32:37276 constructor() {
Tim van der Lippe97611c92020-02-12 16:56:58277 this._button = new UI.Toolbar.ToolbarToggle(Common.UIString.UIString('Element Classes'), '');
Blink Reformat4c46d092018-04-07 15:32:37278 this._button.setText('.cls');
279 this._button.element.classList.add('monospace');
Tim van der Lippe97611c92020-02-12 16:56:58280 this._button.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, this._clicked, this);
Tim van der Lippe13f71fb2019-11-29 11:17:39281 this._view = new ClassesPaneWidget();
Blink Reformat4c46d092018-04-07 15:32:37282 }
283
Andres Olivares43c2d9f2021-02-10 16:48:39284 /**
285 * @param {{forceNew: ?boolean}} opts
286 */
287 static instance(opts = {forceNew: null}) {
288 const {forceNew} = opts;
289 if (!buttonProviderInstance || forceNew) {
290 buttonProviderInstance = new ButtonProvider();
291 }
292
293 return buttonProviderInstance;
294 }
295
Blink Reformat4c46d092018-04-07 15:32:37296 _clicked() {
Tim van der Lippeaabc8302019-12-10 15:34:45297 ElementsPanel.instance().showToolbarPane(!this._view.isShowing() ? this._view : null, this._button);
Blink Reformat4c46d092018-04-07 15:32:37298 }
299
300 /**
301 * @override
Tim van der Lippe97611c92020-02-12 16:56:58302 * @return {!UI.Toolbar.ToolbarItem}
Blink Reformat4c46d092018-04-07 15:32:37303 */
304 item() {
305 return this._button;
306 }
Tim van der Lippe13f71fb2019-11-29 11:17:39307}
Blink Reformat4c46d092018-04-07 15:32:37308
Tim van der Lippe97611c92020-02-12 16:56:58309export class ClassNamePrompt extends UI.TextPrompt.TextPrompt {
Blink Reformat4c46d092018-04-07 15:32:37310 /**
Tim van der Lippe97611c92020-02-12 16:56:58311 * @param {function(!SDK.DOMModel.DOMNode):!Map<string, boolean>} nodeClasses
Blink Reformat4c46d092018-04-07 15:32:37312 */
313 constructor(nodeClasses) {
314 super();
315 this._nodeClasses = nodeClasses;
316 this.initialize(this._buildClassNameCompletions.bind(this), ' ');
317 this.disableDefaultSuggestionForEmptyInput();
Tim van der Lippe32f760f2020-10-01 10:52:15318 /** @type {?string} */
Blink Reformat4c46d092018-04-07 15:32:37319 this._selectedFrameId = '';
320 this._classNamesPromise = null;
321 }
322
323 /**
Tim van der Lippe97611c92020-02-12 16:56:58324 * @param {!SDK.DOMModel.DOMNode} selectedNode
Blink Reformat4c46d092018-04-07 15:32:37325 * @return {!Promise.<!Array.<string>>}
326 */
Mathias Bynens3abc0952020-04-20 14:15:52327 async _getClassNames(selectedNode) {
Blink Reformat4c46d092018-04-07 15:32:37328 const promises = [];
329 const completions = new Set();
330 this._selectedFrameId = selectedNode.frameId();
331
332 const cssModel = selectedNode.domModel().cssModel();
333 const allStyleSheets = cssModel.allStyleSheets();
334 for (const stylesheet of allStyleSheets) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34335 if (stylesheet.frameId !== this._selectedFrameId) {
Blink Reformat4c46d092018-04-07 15:32:37336 continue;
Tim van der Lippe1d6e57a2019-09-30 11:55:34337 }
Mathias Bynens3abc0952020-04-20 14:15:52338 const cssPromise = cssModel.classNamesPromise(stylesheet.id).then(classes => {
339 for (const className of classes) {
340 completions.add(className);
341 }
342 });
Blink Reformat4c46d092018-04-07 15:32:37343 promises.push(cssPromise);
344 }
345
Tim van der Lippe32f760f2020-10-01 10:52:15346 const ownerDocumentId = /** @type {number} */ (
347 /** @type {!SDK.DOMModel.DOMDocument} */ (selectedNode.ownerDocument).id);
348
349 const domPromise = selectedNode.domModel().classNamesPromise(ownerDocumentId).then(classes => {
Mathias Bynens3abc0952020-04-20 14:15:52350 for (const className of classes) {
351 completions.add(className);
352 }
353 });
Blink Reformat4c46d092018-04-07 15:32:37354 promises.push(domPromise);
Mathias Bynens3abc0952020-04-20 14:15:52355 await Promise.all(promises);
356 return [...completions];
Blink Reformat4c46d092018-04-07 15:32:37357 }
358
359 /**
360 * @param {string} expression
361 * @param {string} prefix
362 * @param {boolean=} force
363 * @return {!Promise<!UI.SuggestBox.Suggestions>}
364 */
Mathias Bynens41ea2632020-12-24 05:52:49365 async _buildClassNameCompletions(expression, prefix, force) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34366 if (!prefix || force) {
Blink Reformat4c46d092018-04-07 15:32:37367 this._classNamesPromise = null;
Tim van der Lippe1d6e57a2019-09-30 11:55:34368 }
Blink Reformat4c46d092018-04-07 15:32:37369
Tim van der Lipped1a00aa2020-08-19 16:03:56370 const selectedNode = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34371 if (!selectedNode || (!prefix && !force && !expression.trim())) {
Mathias Bynens41ea2632020-12-24 05:52:49372 return [];
Tim van der Lippe1d6e57a2019-09-30 11:55:34373 }
Blink Reformat4c46d092018-04-07 15:32:37374
Tim van der Lippe1d6e57a2019-09-30 11:55:34375 if (!this._classNamesPromise || this._selectedFrameId !== selectedNode.frameId()) {
Blink Reformat4c46d092018-04-07 15:32:37376 this._classNamesPromise = this._getClassNames(selectedNode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34377 }
Blink Reformat4c46d092018-04-07 15:32:37378
Mathias Bynens41ea2632020-12-24 05:52:49379 let completions = await this._classNamesPromise;
380 const classesMap = this._nodeClasses(/** @type {!SDK.DOMModel.DOMNode} */ (selectedNode));
381 completions = completions.filter(value => !classesMap.get(value));
Blink Reformat4c46d092018-04-07 15:32:37382
Mathias Bynens41ea2632020-12-24 05:52:49383 if (prefix[0] === '.') {
384 completions = completions.map(value => '.' + value);
385 }
386 return completions.filter(value => value.startsWith(prefix)).sort().map(completion => {
387 return {
388 text: completion,
389 title: undefined,
390 subtitle: undefined,
391 iconType: undefined,
392 priority: undefined,
393 isSecondary: undefined,
394 subtitleRenderer: undefined,
395 selectionRange: undefined,
396 hideGhostText: undefined,
397 iconElement: undefined,
398 };
Blink Reformat4c46d092018-04-07 15:32:37399 });
400 }
Tim van der Lippe13f71fb2019-11-29 11:17:39401}