Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: optionally reset existing submenus #104

Merged
merged 2 commits into from
Feb 11, 2025
Merged
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
103 changes: 0 additions & 103 deletions src/cli/commands/scan-for-menus-command.js

This file was deleted.

114 changes: 114 additions & 0 deletions src/cli/commands/scan-for-menus-command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// # scan-for-menus-command.js
import PQueue from 'p-queue';
import ora, { type Ora } from 'ora';
import chalk from 'chalk';
import config from '#cli/config.js';
import logger from '#cli/logger.js';
import { getAllIds } from '#cli/data/standard-menus.js';
import { DBPF, Exemplar, type Entry } from 'sc4/core';
import { FileScanner } from 'sc4/plugins';
import type { TGIArray } from 'sc4/types';
import type LText from 'src/core/ltext.js';

type Menu = {
id: number;
parent?: number;
name: string;
order?: number;
};

type ScanForMenusOptions = {
override?: boolean;
};

// # scanForMenus()
// Performs a scan of the user's plugin folder and reports any submenus found in
// it.
export async function scanForMenus(
folder = process.env.SC4_PLUGINS,
opts: ScanForMenusOptions = {},
) {
const queue = new PQueue({ concurrency: 4096 });
let glob = new FileScanner('**/*', { cwd: folder });
let tasks = [];
const spinner = ora();
spinner.start();
let menus: Menu[] = [];
for await (let file of glob) {
let task = queue.add(async () => {
let dbpf = new DBPF({ file, parse: false });
await dbpf.parseAsync();
dbpf.createIndex();
let subtasks = [];
for (let entry of dbpf.exemplars) {
let subtask = queue.add(() => getMenu(entry, menus, spinner));
subtasks.push(subtask);
}
await Promise.allSettled(subtasks);
});
tasks.push(task);
}
await Promise.allSettled(tasks);
spinner.stop();

// Now that we have all menus, we still need to filter out any of the
// standard menus.
let set = new Set(getAllIds());
menus = menus.filter(menu => !set.has(menu.id));
logger.ok(`Found ${menus.length} menus`);

// Now merge with the existing menus as stored in the config, but make sure
// to not override!
let configMenus = config.get('menus') || [];
let map = new Map();
if (!opts.override) {
for (let menu of configMenus) {
map.set(menu.id, menu);
}
}
let added = 0;
for (let menu of menus) {
if (!map.has(menu.id)) {
map.set(menu.id, menu);
added++;
}
}
if (map.size > 0) {
config.set('menus', [...map.values()]);
} else {
config.delete('menus');
}
if (opts.override) {
logger.ok(`Added ${added} menus to your menu config`);
} else {
logger.ok(`Added ${added} new menus to your menu config`);
}

}

async function getMenu(entry: Entry<Exemplar>, menus: Menu[], spinner: Ora) {
spinner.text = `Scanning ${chalk.cyan(entry.dbpf.file)}`;
let exemplar;
try {
exemplar = await entry.readAsync();
} catch {
return;
}
let parent = exemplar.get('ItemSubmenuParentId');
if (!parent) return null;
let { instance: id } = entry;
let uvnk = exemplar.get('UserVisibleNameKey');
let name = '';
if (uvnk) {
let ltext = entry.dbpf.find(uvnk as TGIArray);
if (!ltext) return;
({ value: name } = await ltext.readAsync() as LText);
}
menus.push({
id,
parent,
name: name.trim(),
order: exemplar.get('ItemOrder'),
});

}
6 changes: 5 additions & 1 deletion src/cli/flows/scan-for-menus-flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,9 @@ export async function scanForMenus() {
filter: info => info.isDirectory(),
});
}
return [plugins];
let override = await prompts.confirm({
message: `Do you want to reset your current submenus configuration? If you choose "Yes", then only the submenus found by this command will be available when adding lots to a submenu.`,
default: false,
});
return [plugins, { override }];
}
8 changes: 8 additions & 0 deletions src/core/dbpf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,14 @@ export default class DBPF {
}
}

// ## createIndex()
// We no longer automatically index all entries in the dbpf. Instead it
// needs to be called manually. It's still advised to do on large dbpf files.
createIndex() {
this.entries.build();
return this;
}

// ## save(opts)
// Saves the DBPF to a file. Note: we're going to do this in a sync way,
// it's just easier.
Expand Down
Loading