| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| /** |
| * @unrestricted |
| */ |
| export class CommandMenu { |
| constructor() { |
| this._commands = []; |
| this._loadCommands(); |
| } |
| |
| /** |
| * @param {string} category |
| * @param {string} keys |
| * @param {string} title |
| * @param {string} shortcut |
| * @param {function()} executeHandler |
| * @param {function()=} availableHandler |
| * @return {!QuickOpen.CommandMenu.Command} |
| */ |
| static createCommand(category, keys, title, shortcut, executeHandler, availableHandler) { |
| // Get localized keys and separate by null character to prevent fuzzy matching from matching across them. |
| const keyList = keys.split(','); |
| let key = ''; |
| keyList.forEach(k => { |
| key += (ls(k.trim()) + '\0'); |
| }); |
| |
| return new Command(category, title, key, shortcut, executeHandler, availableHandler); |
| } |
| |
| /** |
| * @param {!Root.Runtime.Extension} extension |
| * @param {string} title |
| * @param {V} value |
| * @return {!QuickOpen.CommandMenu.Command} |
| * @template V |
| */ |
| static createSettingCommand(extension, title, value) { |
| const category = extension.descriptor()['category'] || ''; |
| const tags = extension.descriptor()['tags'] || ''; |
| const setting = Common.settings.moduleSetting(extension.descriptor()['settingName']); |
| return QuickOpen.CommandMenu.createCommand( |
| ls(category), tags, title, '', setting.set.bind(setting, value), availableHandler); |
| |
| /** |
| * @return {boolean} |
| */ |
| function availableHandler() { |
| return setting.get() !== value; |
| } |
| } |
| |
| /** |
| * @param {!UI.Action} action |
| * @return {!QuickOpen.CommandMenu.Command} |
| */ |
| static createActionCommand(action) { |
| const shortcut = UI.shortcutRegistry.shortcutTitleForAction(action.id()) || ''; |
| return QuickOpen.CommandMenu.createCommand( |
| action.category(), action.tags(), action.title(), shortcut, action.execute.bind(action)); |
| } |
| |
| /** |
| * @param {!Root.Runtime.Extension} extension |
| * @param {string} category |
| * @return {!QuickOpen.CommandMenu.Command} |
| */ |
| static createRevealViewCommand(extension, category) { |
| const viewId = extension.descriptor()['id']; |
| const executeHandler = UI.viewManager.showView.bind(UI.viewManager, viewId); |
| const tags = extension.descriptor()['tags'] || ''; |
| return QuickOpen.CommandMenu.createCommand( |
| category, tags, Common.UIString('Show %s', extension.title()), '', executeHandler); |
| } |
| |
| _loadCommands() { |
| const locations = new Map(); |
| self.runtime.extensions(UI.ViewLocationResolver).forEach(extension => { |
| const category = extension.descriptor()['category']; |
| const name = extension.descriptor()['name']; |
| if (category && name) { |
| locations.set(name, category); |
| } |
| }); |
| const viewExtensions = self.runtime.extensions('view'); |
| for (const extension of viewExtensions) { |
| const category = locations.get(extension.descriptor()['location']); |
| if (category) { |
| this._commands.push(QuickOpen.CommandMenu.createRevealViewCommand(extension, ls(category))); |
| } |
| } |
| |
| // Populate whitelisted settings. |
| const settingExtensions = self.runtime.extensions('setting'); |
| for (const extension of settingExtensions) { |
| const options = extension.descriptor()['options']; |
| if (!options || !extension.descriptor()['category']) { |
| continue; |
| } |
| for (const pair of options) { |
| this._commands.push(QuickOpen.CommandMenu.createSettingCommand(extension, ls(pair['title']), pair['value'])); |
| } |
| } |
| } |
| |
| /** |
| * @return {!Array.<!QuickOpen.CommandMenu.Command>} |
| */ |
| commands() { |
| return this._commands; |
| } |
| } |
| |
| export class CommandMenuProvider extends QuickOpen.FilteredListWidget.Provider { |
| constructor() { |
| super(); |
| this._commands = []; |
| } |
| |
| /** |
| * @override |
| */ |
| attach() { |
| const allCommands = commandMenu.commands(); |
| |
| // Populate whitelisted actions. |
| const actions = UI.actionRegistry.availableActions(); |
| for (const action of actions) { |
| if (action.category()) { |
| this._commands.push(QuickOpen.CommandMenu.createActionCommand(action)); |
| } |
| } |
| |
| for (const command of allCommands) { |
| if (command.available()) { |
| this._commands.push(command); |
| } |
| } |
| |
| this._commands = this._commands.sort(commandComparator); |
| |
| /** |
| * @param {!QuickOpen.CommandMenu.Command} left |
| * @param {!QuickOpen.CommandMenu.Command} right |
| * @return {number} |
| */ |
| function commandComparator(left, right) { |
| const cats = left.category().compareTo(right.category()); |
| return cats ? cats : left.title().compareTo(right.title()); |
| } |
| } |
| |
| /** |
| * @override |
| */ |
| detach() { |
| this._commands = []; |
| } |
| |
| /** |
| * @override |
| * @return {number} |
| */ |
| itemCount() { |
| return this._commands.length; |
| } |
| |
| /** |
| * @override |
| * @param {number} itemIndex |
| * @return {string} |
| */ |
| itemKeyAt(itemIndex) { |
| return this._commands[itemIndex].key(); |
| } |
| |
| /** |
| * @override |
| * @param {number} itemIndex |
| * @param {string} query |
| * @return {number} |
| */ |
| itemScoreAt(itemIndex, query) { |
| const command = this._commands[itemIndex]; |
| const opcodes = Diff.Diff.charDiff(query.toLowerCase(), command.title().toLowerCase()); |
| let score = 0; |
| // Score longer sequences higher. |
| for (let i = 0; i < opcodes.length; ++i) { |
| if (opcodes[i][0] === Diff.Diff.Operation.Equal) { |
| score += opcodes[i][1].length * opcodes[i][1].length; |
| } |
| } |
| |
| // Score panel/drawer reveals above regular actions. |
| if (command.category().startsWith('Panel')) { |
| score += 2; |
| } else if (command.category().startsWith('Drawer')) { |
| score += 1; |
| } |
| |
| return score; |
| } |
| |
| /** |
| * @override |
| * @param {number} itemIndex |
| * @param {string} query |
| * @param {!Element} titleElement |
| * @param {!Element} subtitleElement |
| */ |
| renderItem(itemIndex, query, titleElement, subtitleElement) { |
| const command = this._commands[itemIndex]; |
| titleElement.removeChildren(); |
| const tagElement = titleElement.createChild('span', 'tag'); |
| const index = String.hashCode(command.category()) % MaterialPaletteColors.length; |
| tagElement.style.backgroundColor = MaterialPaletteColors[index]; |
| tagElement.textContent = command.category(); |
| titleElement.createTextChild(command.title()); |
| QuickOpen.FilteredListWidget.highlightRanges(titleElement, query, true); |
| subtitleElement.textContent = command.shortcut(); |
| } |
| |
| /** |
| * @override |
| * @param {?number} itemIndex |
| * @param {string} promptValue |
| */ |
| selectItem(itemIndex, promptValue) { |
| if (itemIndex === null) { |
| return; |
| } |
| this._commands[itemIndex].execute(); |
| Host.userMetrics.actionTaken(Host.UserMetrics.Action.SelectCommandFromCommandMenu); |
| } |
| |
| /** |
| * @override |
| * @return {string} |
| */ |
| notFoundText() { |
| return ls`No commands found`; |
| } |
| } |
| |
| export const MaterialPaletteColors = [ |
| '#F44336', '#E91E63', '#9C27B0', '#673AB7', '#3F51B5', '#03A9F4', '#00BCD4', '#009688', '#4CAF50', '#8BC34A', |
| '#CDDC39', '#FFC107', '#FF9800', '#FF5722', '#795548', '#9E9E9E', '#607D8B' |
| ]; |
| |
| /** |
| * @unrestricted |
| */ |
| export class Command { |
| /** |
| * @param {string} category |
| * @param {string} title |
| * @param {string} key |
| * @param {string} shortcut |
| * @param {function()} executeHandler |
| * @param {function()=} availableHandler |
| */ |
| constructor(category, title, key, shortcut, executeHandler, availableHandler) { |
| this._category = category; |
| this._title = title; |
| this._key = category + '\0' + title + '\0' + key; |
| this._shortcut = shortcut; |
| this._executeHandler = executeHandler; |
| this._availableHandler = availableHandler; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| category() { |
| return this._category; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| title() { |
| return this._title; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| key() { |
| return this._key; |
| } |
| |
| /** |
| * @return {string} |
| */ |
| shortcut() { |
| return this._shortcut; |
| } |
| |
| /** |
| * @return {boolean} |
| */ |
| available() { |
| return this._availableHandler ? this._availableHandler() : true; |
| } |
| |
| execute() { |
| this._executeHandler(); |
| } |
| } |
| |
| |
| /** |
| * @implements {UI.ActionDelegate} |
| * @unrestricted |
| */ |
| export class ShowActionDelegate { |
| /** |
| * @override |
| * @param {!UI.Context} context |
| * @param {string} actionId |
| * @return {boolean} |
| */ |
| handleAction(context, actionId) { |
| Host.InspectorFrontendHost.bringToFront(); |
| QuickOpen.QuickOpen.show('>'); |
| return true; |
| } |
| } |
| |
| /* Legacy exported object */ |
| self.QuickOpen = self.QuickOpen || {}; |
| |
| /* Legacy exported object */ |
| QuickOpen = QuickOpen || {}; |
| |
| /** |
| * @constructor |
| */ |
| QuickOpen.CommandMenu = CommandMenu; |
| |
| /** |
| * @constructor |
| */ |
| QuickOpen.CommandMenu.Command = Command; |
| |
| /** |
| * @constructor |
| */ |
| QuickOpen.CommandMenu.ShowActionDelegate = ShowActionDelegate; |
| |
| /** |
| * @constructor |
| */ |
| QuickOpen.CommandMenuProvider = CommandMenuProvider; |
| QuickOpen.CommandMenuProvider.MaterialPaletteColors = MaterialPaletteColors; |
| |
| /** @type {!QuickOpen.CommandMenu} */ |
| const commandMenu = new CommandMenu(); |
| QuickOpen.commandMenu = commandMenu; |