diff --git a/packages/koishi-core/src/command.ts b/packages/koishi-core/src/command.ts index 5c2cbda296..e36f933683 100644 --- a/packages/koishi-core/src/command.ts +++ b/packages/koishi-core/src/command.ts @@ -102,7 +102,10 @@ type Extend = { [P in K | keyof O]: (P extends keyof O ? O[P] : unknown) & (P extends K ? T : unknown) } -type ArgvInferred = Iterable | ((argv: ParsedArgv, fields: Set) => Iterable) +export type FieldCollector = + | Iterable + | ((argv: ParsedArgv, fields: Set) => void) + export type CommandAction = (this: Command, config: ParsedArgv, ...args: string[]) => void | string | Promise @@ -117,36 +120,37 @@ export class Command = {} private _optionSymbolMap: Record = {} - private _userFields: ArgvInferred[] = [] - private _groupFields: ArgvInferred[] = [] + private _userFields: FieldCollector<'user'>[] = [] + private _groupFields: FieldCollector<'group'>[] = [] _action?: CommandAction static defaultConfig: CommandConfig = {} static defaultOptionConfig: OptionConfig = {} - private static _userFields: ArgvInferred[] = [] - private static _groupFields: ArgvInferred[] = [] + private static _userFields: FieldCollector<'user'>[] = [] + private static _groupFields: FieldCollector<'group'>[] = [] - static userFields(fields: ArgvInferred) { + static userFields(fields: FieldCollector<'user'>) { this._userFields.push(fields) return this } - static groupFields(fields: ArgvInferred) { + static groupFields(fields: FieldCollector<'group'>) { this._groupFields.push(fields) return this } static collect(argv: ParsedArgv, key: T, fields = new Set()) { if (!argv) return - const values: ArgvInferred[] = [ + const values: FieldCollector[] = [ ...this[`_${key}Fields`], ...argv.command[`_${key}Fields`], ] - for (let value of values) { + for (const value of values) { if (typeof value === 'function') { - value = value(argv, fields) + value(argv, fields) + continue } for (const field of value) { fields.add(field) @@ -183,18 +187,14 @@ export class Command` } - userFields(fields: Iterable): Command - userFields(fields: (argv: ParsedArgv, fields: Set) => Iterable): Command - userFields(fields: ArgvInferred) { + userFields(fields: FieldCollector<'user', T, O>): Command { this._userFields.push(fields) - return this + return this as any } - groupFields(fields: Iterable): Command - groupFields(fields: (argv: ParsedArgv, fields: Set) => Iterable): Command - groupFields(fields: ArgvInferred) { + groupFields(fields: FieldCollector<'group', T, O>): Command { this._groupFields.push(fields) - return this + return this as any } alias(...names: string[]) { diff --git a/packages/koishi-core/src/plugins/help.ts b/packages/koishi-core/src/plugins/help.ts index 080c6e4a15..091eb2f6a8 100644 --- a/packages/koishi-core/src/plugins/help.ts +++ b/packages/koishi-core/src/plugins/help.ts @@ -1,6 +1,6 @@ import { getUsage, getUsageName, ValidationField } from './validate' import { User, Group, TableType, Tables } from '../database' -import { Command, ParsedArgv } from '../command' +import { Command, FieldCollector, ParsedArgv } from '../command' import { Session } from '../session' import { App } from '../app' import { Message } from './message' @@ -59,17 +59,15 @@ export default function apply(app: App) { return '' }) - function createCollector(key: T) { - return function* (argv: ParsedArgv, fields: Set) { - const { args: [name] } = argv - const command = app._commandMap[name] || app._shortcutMap[name] - if (!command) return - yield* Command.collect({ ...argv, args: [], options: { help: true } }, key, fields) - } + const createCollector = (key: T): FieldCollector => (argv, fields) => { + const { args: [name] } = argv + const command = app._commandMap[name] || app._shortcutMap[name] + if (!command) return + Command.collect({ ...argv, command, args: [], options: { help: true } }, key, fields) } app.command('help [command]', '显示帮助信息', { authority: 0 }) - .userFields(['authority']) + .userFields(['authority']) .userFields(createCollector('user')) .groupFields(createCollector('group')) .shortcut('帮助', { fuzzy: true }) @@ -98,6 +96,7 @@ export default function apply(app: App) { }, }) } + return showHelp(command, session, options) }) } diff --git a/packages/koishi-core/src/plugins/validate.ts b/packages/koishi-core/src/plugins/validate.ts index 57275137ea..007ba3f524 100644 --- a/packages/koishi-core/src/plugins/validate.ts +++ b/packages/koishi-core/src/plugins/validate.ts @@ -59,7 +59,7 @@ Object.assign(Command.defaultOptionConfig, { authority: 0, }) -Command.userFields(function* ({ command, options = {} }, fields) { +Command.userFields(({ command, options = {} }, fields) => { const { maxUsage, minInterval, authority } = command.config let shouldFetchAuthority = !fields.has('authority') && authority > 0 let shouldFetchUsage = !!(maxUsage || minInterval) @@ -69,10 +69,10 @@ Command.userFields(function* ({ command, options = {} }, fields) { if (notUsage) shouldFetchUsage = false } } - if (shouldFetchAuthority) yield 'authority' + if (shouldFetchAuthority) fields.add('authority') if (shouldFetchUsage) { - if (maxUsage) yield 'usage' - if (minInterval) yield 'timers' + if (maxUsage) fields.add('usage') + if (minInterval) fields.add('timers') } }) diff --git a/packages/koishi-core/tests/command.spec.ts b/packages/koishi-core/tests/command.spec.ts index 2221ec5dae..1fec565cf4 100644 --- a/packages/koishi-core/tests/command.spec.ts +++ b/packages/koishi-core/tests/command.spec.ts @@ -1,12 +1,11 @@ import { App } from 'koishi-test-utils' +import { inspect } from 'util' import { expect } from 'chai' import '@shigma/chai-extended' -let app: App - describe('Command API', () => { describe('Register Commands', () => { - before(() => app = new App()) + const app = new App() it('constructor checks', () => { expect(() => app.command('')).to.throw() @@ -26,6 +25,10 @@ describe('Command API', () => { expect(app._commandMap.c.context).to.equal(ctx2) }) + it('custom inspect', () => { + expect(inspect(app.command('a'))).to.equal('Command ') + }) + it('modify commands', () => { const d1 = app.command('d', 'foo', { authority: 1 }) expect(app._commandMap.d.config.description).to.equal('foo') @@ -63,6 +66,7 @@ describe('Command API', () => { }) describe('Register Subcommands', () => { + let app: App beforeEach(() => app = new App()) it('command.subcommand', () => { @@ -123,4 +127,28 @@ describe('Command API', () => { expect(() => app.command('a/d')).not.to.throw() }) }) + + describe('Dispose Commands', () => { + const app = new App() + const foo = app.command('foo') + const bar = foo.subcommand('bar') + const test = bar.subcommand('test') + bar.alias('baz').shortcut('1') + test.alias('it').shortcut('2') + + it('before dispose', () => { + // don't forget help + expect(app._commands).to.have.length(4) + expect(app._shortcuts).to.have.length(3) + expect(foo.children).to.have.length(1) + }) + + it('after dispose', () => { + bar.dispose() + // don't forget help + expect(app._commands).to.have.length(2) + expect(app._shortcuts).to.have.length(1) + expect(foo.children).to.have.length(0) + }) + }) }) diff --git a/packages/koishi-core/tests/parser.spec.ts b/packages/koishi-core/tests/parser.spec.ts index 000d151c4c..fc952ee757 100644 --- a/packages/koishi-core/tests/parser.spec.ts +++ b/packages/koishi-core/tests/parser.spec.ts @@ -1,6 +1,5 @@ import { Command } from 'koishi-core' import { App } from 'koishi-test-utils' -import { inspect } from 'util' import { expect } from 'chai' import '@shigma/chai-extended' @@ -10,28 +9,23 @@ let cmd: Command describe('Parser API', () => { describe('Basic Support', () => { - it('register', () => { - // there is a built-in help command - expect(app._commands).to.have.length(1) - - cmd = app.command('cmd1 [...bar]') - expect(app._commands).to.have.length(2) - }) - - it('inspect', () => { - expect(inspect(cmd)).to.equal('Command ') - }) - it('parse arguments', () => { + cmd = app.command('cmd1 [...bar]') expect(cmd.parse('')).to.have.shape({ args: [] }) expect(cmd.parse('a')).to.have.shape({ args: ['a'] }) expect(cmd.parse('a b')).to.have.shape({ args: ['a', 'b'] }) expect(cmd.parse('a b c')).to.have.shape({ args: ['a', 'b', 'c'] }) }) - it('dispose', () => { - cmd.dispose() - expect(app._commands).to.have.length(1) + it('stringify arguments', () => { + cmd = app.command('cmd4') + cmd.option('alpha', '-a ') + cmd.option('beta', '-b') + expect(cmd.stringify(['foo', 'bar'], {})).to.equal('cmd4 foo bar') + expect(cmd.stringify([], { alpha: 2 })).to.equal('cmd4 --alpha 2') + expect(cmd.stringify([], { alpha: ' ' })).to.equal('cmd4 --alpha " "') + expect(cmd.stringify([], { beta: true })).to.equal('cmd4 --beta') + expect(cmd.stringify([], { beta: false })).to.equal('cmd4 --no-beta') }) }) @@ -140,17 +134,4 @@ describe('Parser API', () => { expect(cmd.parse('-- "foo;bar";baz', ';')).to.have.shape({ options: { rest: 'foo;bar' }, rest: ';baz' }) }) }) - - describe('Stringify Argv', () => { - it('basic support', () => { - cmd = app.command('cmd4') - cmd.option('alpha', '-a ') - cmd.option('beta', '-b') - expect(cmd.stringify(['foo', 'bar'], {})).to.equal('cmd4 foo bar') - expect(cmd.stringify([], { alpha: 2 })).to.equal('cmd4 --alpha 2') - expect(cmd.stringify([], { alpha: ' ' })).to.equal('cmd4 --alpha " "') - expect(cmd.stringify([], { beta: true })).to.equal('cmd4 --beta') - expect(cmd.stringify([], { beta: false })).to.equal('cmd4 --no-beta') - }) - }) })