Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Chip from '../../UI/Chip';
import TextField from '@material-ui/core/TextField';
import ChevronRightIcon from '../../UI/CustomSvgIcons/ChevronArrowRight';
import Autocomplete from '@material-ui/lab/Autocomplete';
import filterOptions from './FilterOptions';
import makeFilterOptions from './FilterOptions';
import {
type NamedCommand,
type CommandOption,
Expand Down Expand Up @@ -134,6 +134,10 @@ const AutocompletePicker = (
const [open, setOpen] = React.useState(true);
const shortcutMap = useShortcutMap();
const classes = useStyles();
const filterOptions = React.useMemo(
() => makeFilterOptions(props.i18n),
[props.i18n]
);

// $FlowFixMe[missing-local-annot]
const handleClose = (_, reason) => {
Expand Down
82 changes: 65 additions & 17 deletions newIDE/app/src/CommandPalette/CommandPalette/FilterOptions.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,76 @@
// @flow
import { t } from '@lingui/macro';
import { type I18n as I18nType } from '@lingui/core';
import { fuzzyOrEmptyFilter } from '../../Utils/FuzzyOrEmptyFilter';

/**
* Filters options both simply and fuzzy-ly,
* prioritizing simple-matched options
* Words that should be treated as synonyms when searching for commands.
* For example, "Edit scene variables" and "Open scene variables" should both match.
* Uses i18n so that synonyms work in all languages.
*/
const filterOptions = <T: Object>(
const getSynonymGroups = (i18n: I18nType): Array<Array<string>> => [
[i18n._(t`open`), i18n._(t`edit`)],
];

/**
* Returns an alternate version of the search text with synonyms swapped,
* or null if no synonym applies.
*/
const getSearchTextWithSynonym = (
searchText: string,
i18n: I18nType
): string | null => {
const synonymGroups = getSynonymGroups(i18n);
for (const group of synonymGroups) {
for (let i = 0; i < group.length; i++) {
const word = group[i];
if (searchText.startsWith(word + ' ') || searchText === word) {
// Replace the first occurrence of the synonym with the other synonym.
for (let j = 0; j < group.length; j++) {
if (i !== j) {
return group[j] + searchText.slice(word.length);
}
}
}
}
}
return null;
};

/**
* Creates a filter function that filters options both simply and fuzzy-ly,
* prioritizing simple-matched options.
* Accepts an i18n instance so that synonym groups can be translated.
*/
const makeFilterOptions = (
i18n: I18nType
): (<T: Object>(
options: Array<T>,
state: { getOptionLabel: T => string, inputValue: string }
): any => {
const searchText = state.inputValue.toLowerCase();
if (searchText === '') return options;
) => any) => {
return <T: Object>(
options: Array<T>,
state: { getOptionLabel: T => string, inputValue: string }
): any => {
const searchText = state.inputValue.toLowerCase();
if (searchText === '') return options;

const synonymSearchText = getSearchTextWithSynonym(searchText, i18n);

const directMatches = [];
const fuzzyMatches = [];
options.forEach(option => {
if (option.hit) return directMatches.push(option);
const optionText = state.getOptionLabel(option).toLowerCase();
if (optionText.includes(searchText)) return directMatches.push(option);
if (fuzzyOrEmptyFilter(searchText, optionText))
return fuzzyMatches.push(option);
});
const directMatches = [];
const fuzzyMatches = [];
options.forEach(option => {
if (option.hit) return directMatches.push(option);
const optionText = state.getOptionLabel(option).toLowerCase();
if (optionText.includes(searchText)) return directMatches.push(option);
if (synonymSearchText && optionText.includes(synonymSearchText))
return directMatches.push(option);
if (fuzzyOrEmptyFilter(searchText, optionText))
return fuzzyMatches.push(option);
});

return [...directMatches, ...fuzzyMatches];
return [...directMatches, ...fuzzyMatches];
};
};

export default filterOptions;
export default makeFilterOptions;
30 changes: 29 additions & 1 deletion newIDE/app/src/CommandPalette/CommandsList.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ export type CommandName =
| 'OPEN_SETUP_GRID'
| 'EDIT_LAYER_EFFECTS'
| 'EDIT_LAYER'
| 'EDIT_NETWORK_PREVIEW'
| 'EDIT_OBJECT'
| 'EDIT_OBJECT_BEHAVIORS'
| 'EDIT_OBJECT_EFFECTS'
Expand All @@ -68,6 +67,11 @@ export type CommandName =
| 'SEARCH_EVENTS'
| 'OPEN_EXTENSION_SETTINGS'
| 'OPEN_PROFILE'
| 'OPEN_PREFERENCES'
| 'OPEN_ABOUT'
| 'OPEN_LANGUAGE'
| 'OPEN_VERSION_HISTORY'
| 'OPEN_DEBUGGER'
| 'OPEN_MEMORY_TRACKER_REGISTRY';

export const commandAreas = {
Expand Down Expand Up @@ -350,6 +354,30 @@ const commandsList: { [CommandName]: CommandMetadata } = {
displayText: t`Open extension settings`,
},

// IDE commands
OPEN_PREFERENCES: {
area: 'IDE',
displayText: t`Open preferences`,
},
OPEN_ABOUT: {
area: 'IDE',
displayText: t`Open "About GDevelop" (version)`,
},
OPEN_LANGUAGE: {
area: 'IDE',
displayText: t`Change language`,
},

// Project commands
OPEN_VERSION_HISTORY: {
area: 'PROJECT',
displayText: t`Open version history`,
},
OPEN_DEBUGGER: {
area: 'PROJECT',
displayText: t`Open debugger`,
},

// Debug commands
OPEN_MEMORY_TRACKER_REGISTRY: {
area: 'IDE',
Expand Down
25 changes: 25 additions & 0 deletions newIDE/app/src/MainFrame/MainFrameCommands.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ type CommandHandlers = {|
onRestartInGameEditor: (reason: string) => void,
onOpenGlobalSearch: () => void,
onOpenMemoryTrackerRegistry: () => void,
onOpenPreferences: () => void,
onOpenAbout: () => void,
onOpenLanguage: () => void,
onOpenVersionHistory: () => void,
onOpenDebugger: () => void,
|};

const useMainFrameCommands = (handlers: CommandHandlers) => {
Expand Down Expand Up @@ -174,6 +179,26 @@ const useMainFrameCommands = (handlers: CommandHandlers) => {
),
});

useCommand('OPEN_PREFERENCES', true, {
handler: handlers.onOpenPreferences,
});

useCommand('OPEN_ABOUT', true, {
handler: handlers.onOpenAbout,
});

useCommand('OPEN_LANGUAGE', true, {
handler: handlers.onOpenLanguage,
});

useCommand('OPEN_VERSION_HISTORY', !!handlers.project, {
handler: handlers.onOpenVersionHistory,
});

useCommand('OPEN_DEBUGGER', !!handlers.project, {
handler: handlers.onOpenDebugger,
});

useCommand('OPEN_MEMORY_TRACKER_REGISTRY', true, {
handler: handlers.onOpenMemoryTrackerRegistry,
});
Expand Down
5 changes: 5 additions & 0 deletions newIDE/app/src/MainFrame/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4815,6 +4815,11 @@ const MainFrame = (props: Props): React.MixedElement => {
onRestartInGameEditor,
onOpenGlobalSearch: openGlobalSearch,
onOpenMemoryTrackerRegistry: () => setMemoryTrackedRegistryDialogOpen(true),
onOpenPreferences: () => openPreferencesDialog(true),
onOpenAbout: () => openAboutDialog(true),
onOpenLanguage: () => openLanguageDialog(true),
onOpenVersionHistory: openVersionHistoryPanel,
onOpenDebugger: openDebugger,
});

const resourceManagementProps: ResourceManagementProps = React.useMemo(
Expand Down
Loading