blob: a993298eeb03fdf88f4f094071a492c340ba54fb [file] [log] [blame]
// 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;