123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- function splitArray<T> (arr: T[], predicate: (x: T) => boolean) {
- const result = arr.reduce((multi, item) => {
- const current = multi[multi.length - 1];
- if (predicate(item)) {
- if (current.length > 0) multi.push([]);
- } else {
- current.push(item);
- }
- return multi;
- }, [[]] as T[][]);
- if (result[result.length - 1].length === 0) {
- return result.slice(0, result.length - 1);
- }
- return result;
- }
- function joinArrays (arrays: any[][], joinIDs: any[]) {
- return arrays.reduce((joined, arr, i) => {
- if (i > 0 && arr.length) {
- if (joinIDs.length > 0) {
- joined.push(joinIDs[0]);
- joinIDs.splice(0, 1);
- } else {
- joined.push({ type: 'separator' });
- }
- }
- return joined.concat(arr);
- }, []);
- }
- function pushOntoMultiMap<K, V> (map: Map<K, V[]>, key: K, value: V) {
- if (!map.has(key)) {
- map.set(key, []);
- }
- map.get(key)!.push(value);
- }
- function indexOfGroupContainingID<T> (groups: {id?: T}[][], id: T, ignoreGroup: {id?: T}[]) {
- return groups.findIndex(
- candidateGroup =>
- candidateGroup !== ignoreGroup &&
- candidateGroup.some(
- candidateItem => candidateItem.id === id
- )
- );
- }
- // Sort nodes topologically using a depth-first approach. Encountered cycles
- // are broken.
- function sortTopologically<T> (originalOrder: T[], edgesById: Map<T, T[]>) {
- const sorted = [] as T[];
- const marked = new Set<T>();
- const visit = (mark: T) => {
- if (marked.has(mark)) return;
- marked.add(mark);
- const edges = edgesById.get(mark);
- if (edges != null) {
- edges.forEach(visit);
- }
- sorted.push(mark);
- };
- originalOrder.forEach(visit);
- return sorted;
- }
- function attemptToMergeAGroup<T> (groups: {before?: T[], after?: T[], id?: T}[][]) {
- for (let i = 0; i < groups.length; i++) {
- const group = groups[i];
- for (const item of group) {
- const toIDs = [...(item.before || []), ...(item.after || [])];
- for (const id of toIDs) {
- const index = indexOfGroupContainingID(groups, id, group);
- if (index === -1) continue;
- const mergeTarget = groups[index];
- mergeTarget.push(...group);
- groups.splice(i, 1);
- return true;
- }
- }
- }
- return false;
- }
- function mergeGroups<T> (groups: {before?: T[], after?: T[], id?: T}[][]) {
- let merged = true;
- while (merged) {
- merged = attemptToMergeAGroup(groups);
- }
- return groups;
- }
- function sortItemsInGroup<T> (group: {before?: T[], after?: T[], id?: T}[]) {
- const originalOrder = group.map((node, i) => i);
- const edges = new Map();
- const idToIndex = new Map(group.map((item, i) => [item.id, i]));
- group.forEach((item, i) => {
- if (item.before) {
- item.before.forEach(toID => {
- const to = idToIndex.get(toID);
- if (to != null) {
- pushOntoMultiMap(edges, to, i);
- }
- });
- }
- if (item.after) {
- item.after.forEach(toID => {
- const to = idToIndex.get(toID);
- if (to != null) {
- pushOntoMultiMap(edges, i, to);
- }
- });
- }
- });
- const sortedNodes = sortTopologically(originalOrder, edges);
- return sortedNodes.map(i => group[i]);
- }
- function findEdgesInGroup<T> (groups: {beforeGroupContaining?: T[], afterGroupContaining?: T[], id?: T}[][], i: number, edges: Map<any, any>) {
- const group = groups[i];
- for (const item of group) {
- if (item.beforeGroupContaining) {
- for (const id of item.beforeGroupContaining) {
- const to = indexOfGroupContainingID(groups, id, group);
- if (to !== -1) {
- pushOntoMultiMap(edges, to, i);
- return;
- }
- }
- }
- if (item.afterGroupContaining) {
- for (const id of item.afterGroupContaining) {
- const to = indexOfGroupContainingID(groups, id, group);
- if (to !== -1) {
- pushOntoMultiMap(edges, i, to);
- return;
- }
- }
- }
- }
- }
- function sortGroups<T> (groups: {id?: T}[][]) {
- const originalOrder = groups.map((item, i) => i);
- const edges = new Map();
- for (let i = 0; i < groups.length; i++) {
- findEdgesInGroup(groups, i, edges);
- }
- const sortedGroupIndexes = sortTopologically(originalOrder, edges);
- return sortedGroupIndexes.map(i => groups[i]);
- }
- export function sortMenuItems (menuItems: (Electron.MenuItemConstructorOptions | Electron.MenuItem)[]) {
- const isSeparator = (i: Electron.MenuItemConstructorOptions | Electron.MenuItem) => {
- const opts = i as Electron.MenuItemConstructorOptions;
- return i.type === 'separator' && !opts.before && !opts.after && !opts.beforeGroupContaining && !opts.afterGroupContaining;
- };
- const separators = menuItems.filter(isSeparator);
- // Split the items into their implicit groups based upon separators.
- const groups = splitArray(menuItems, isSeparator);
- const mergedGroups = mergeGroups(groups);
- const mergedGroupsWithSortedItems = mergedGroups.map(sortItemsInGroup);
- const sortedGroups = sortGroups(mergedGroupsWithSortedItems);
- const joined = joinArrays(sortedGroups, separators);
- return joined;
- }
|