Skip to content

Commit

Permalink
fix(core): use map instead of object to store commands, fix #232
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Apr 15, 2021
1 parent 51552f7 commit bc87290
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 37 deletions.
12 changes: 6 additions & 6 deletions packages/koishi-core/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ export class App extends Context {
public registry = new Map<Plugin, Plugin.State>()

_bots = createBots('sid')
_commands: Command[] = []
_commandMap: Record<string, Command> = {}
_commandList: Command[] = []
_commands = new Map<string, Command>()
_shortcuts: Command.Shortcut[] = []
_hooks: Record<keyof any, [Context, (...args: any[]) => any][]> = {}
_userCache: Record<string, LruCache<string, Observed<Partial<User>, Promise<void>>>>
Expand Down Expand Up @@ -122,11 +122,11 @@ export class App extends Context {
if (argv.root && subtype !== 'private' && parsed.prefix === null && !parsed.appel) return
if (!argv.tokens.length) return
const segments = argv.tokens[0].content.split('.')
let i = 1, name = segments[0]
while (this._commandMap[name] && i < segments.length) {
name = this._commandMap[name].name + '.' + segments[i++]
let i = 1, name = segments[0], cmd: Command
while ((cmd = this._commands.get(name)) && i < segments.length) {
name = cmd.name + '.' + segments[i++]
}
if (name in this._commandMap) {
if (cmd) {
argv.tokens.shift()
return name
}
Expand Down
12 changes: 6 additions & 6 deletions packages/koishi-core/src/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export class Command<U extends User.Field = never, G extends Channel.Field = nev
super(name, decl, desc)
this.config = { ...Command.defaultConfig }
this._registerAlias(this.name)
context.app._commands.push(this)
context.app._commandList.push(this)
this.option('help', '-h 显示此信息', { hidden: true })
}

Expand All @@ -110,9 +110,9 @@ export class Command<U extends User.Field = never, G extends Channel.Field = nev
private _registerAlias(name: string) {
name = name.toLowerCase()
this._aliases.push(name)
const previous = this.app._commandMap[name]
const previous = this.app._commands.get(name)
if (!previous) {
this.app._commandMap[name] = this
this.app._commands.set(name, this)
} else if (previous !== this) {
throw new Error(format('duplicate command names: "%s"', name))
}
Expand All @@ -138,7 +138,7 @@ export class Command<U extends User.Field = never, G extends Channel.Field = nev
this._registerAlias(name)
this._disposables?.push(() => {
remove(this._aliases, name)
delete this.app._commandMap[name]
this.app._commands.delete(name)
})
}
return this
Expand Down Expand Up @@ -262,8 +262,8 @@ export class Command<U extends User.Field = never, G extends Channel.Field = nev
cmd.dispose()
}
this.app._shortcuts = this.app._shortcuts.filter(s => s.command !== this)
this._aliases.forEach(name => delete this.app._commandMap[name])
remove(this.app._commands, this)
this._aliases.forEach(name => this.app._commands.delete(name))
remove(this.app._commandList, this)
if (this.parent) {
remove(this.parent.children, this)
}
Expand Down
2 changes: 1 addition & 1 deletion packages/koishi-core/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ export class Context {
segments.forEach((segment, index) => {
const code = segment.charCodeAt(0)
const name = code === 46 ? parent.name + segment : code === 47 ? segment.slice(1) : segment
let command = this.app._commandMap[name]
let command = this.app._commands.get(name)
if (command) {
if (parent) {
if (command === parent) {
Expand Down
9 changes: 5 additions & 4 deletions packages/koishi-core/src/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export default function apply(ctx: Context) {

const app = ctx.app
function findCommand(target: string) {
if (target in app._commandMap) return app._commandMap[target]
const command = app._commands.get(target)
if (command) return command
const shortcut = app._shortcuts.find(({ name }) => {
return typeof name === 'string' ? name === target : name.test(target)
})
Expand All @@ -46,7 +47,7 @@ export default function apply(ctx: Context) {
.option('showHidden', '-H 查看隐藏的选项和指令')
.action(async ({ session, options }, target) => {
if (!target) {
const commands = app._commands.filter(cmd => cmd.parent === null)
const commands = app._commandList.filter(cmd => cmd.parent === null)
const output = formatCommands('internal.global-help-prolog', session, commands, options)
const epilog = template('internal.global-help-epilog')
if (epilog) output.push(epilog)
Expand All @@ -62,7 +63,7 @@ export default function apply(ctx: Context) {
suffix: template('internal.help-suggestion-suffix'),
async apply(suggestion) {
await this.observeUser(['authority', 'usage', 'timers'])
const output = await showHelp(app._commandMap[suggestion], this as any, options)
const output = await showHelp(app._commands.get(suggestion), this as any, options)
return session.send(output)
},
})
Expand All @@ -74,7 +75,7 @@ export default function apply(ctx: Context) {
}

export function getCommandNames(session: Session) {
return session.app._commands
return session.app._commandList
.filter(cmd => cmd.match(session) && !cmd.config.hidden)
.flatMap(cmd => cmd._aliases)
}
Expand Down
4 changes: 2 additions & 2 deletions packages/koishi-core/src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ export class Session<
resolve(argv: Argv) {
if (!argv.command) {
const { name = this.app.bail('parse', argv, this) } = argv
if (!(argv.command = this.app._commandMap[name])) return
if (!(argv.command = this.app._commands.get(name))) return
}
if (argv.tokens?.every(token => !token.inters.length)) {
const { options, args, error } = argv.command.parse(argv)
Expand Down Expand Up @@ -351,7 +351,7 @@ export class Session<
}
if (!this.resolve(argv)) return ''
} else {
argv.command ||= this.app._commandMap[argv.name]
argv.command ||= this.app._commands.get(argv.name)
if (!argv.command) {
logger.warn(new Error(`cannot find command ${argv.name}`))
return ''
Expand Down
32 changes: 16 additions & 16 deletions packages/koishi-core/tests/command.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ describe('Command API', () => {
ctx2.command('c')

// a, b, c, help
expect(app._commands).to.have.length(4)
expect(app._commandMap.a.context).to.equal(app)
expect(app._commandMap.b.context).to.equal(ctx1)
expect(app._commandMap.c.context).to.equal(ctx2)
expect(app._commandList).to.have.length(4)
expect(app._commands.get('a').context).to.equal(app)
expect(app._commands.get('b').context).to.equal(ctx1)
expect(app._commands.get('c').context).to.equal(ctx2)
})

it('custom inspect', () => {
Expand All @@ -35,12 +35,12 @@ describe('Command API', () => {

it('modify commands', () => {
const d1 = app.command('d', 'foo', { authority: 1 })
expect(app._commandMap.d.description).to.equal('foo')
expect(app._commandMap.d.config.authority).to.equal(1)
expect(app._commands.get('d').description).to.equal('foo')
expect(app._commands.get('d').config.authority).to.equal(1)

const d2 = app.command('d', 'bar', { authority: 2 })
expect(app._commandMap.d.description).to.equal('bar')
expect(app._commandMap.d.config.authority).to.equal(2)
expect(app._commands.get('d').description).to.equal('bar')
expect(app._commands.get('d').config.authority).to.equal(2)

expect(d1).to.equal(d2)
})
Expand Down Expand Up @@ -142,11 +142,11 @@ describe('Command API', () => {

it('basic support', () => {
// don't forget help
expect(app._commands).to.have.length(4)
expect(app._commandList).to.have.length(4)
expect(app._shortcuts).to.have.length(3)
expect(foo.children).to.have.length(1)
bar.dispose()
expect(app._commands).to.have.length(2)
expect(app._commandList).to.have.length(2)
expect(app._shortcuts).to.have.length(1)
expect(foo.children).to.have.length(0)
})
Expand All @@ -156,17 +156,17 @@ describe('Command API', () => {
ctx.command('foo', 'desc', { patch: true }).alias('fooo').option('opt', 'option 1')
ctx.command('abc', 'desc', { patch: true }).alias('abcd').option('opt', 'option 1')

const { foo, fooo, abc, abcd } = app._commandMap
const foo = app._commands.get('foo')
expect(foo).to.be.ok
expect(fooo).to.be.ok
expect(app._commands.get('fooo')).to.be.ok
expect(foo.description).to.equal('desc')
expect(Object.keys(foo._options)).to.have.length(2)
expect(abc).to.be.undefined
expect(abcd).to.be.undefined
expect(app._commands.get('abc')).to.be.undefined
expect(app._commands.get('abcd')).to.be.undefined

ctx.dispose()
expect(app._commandMap.foo).to.be.ok
expect(app._commandMap.fooo).to.be.undefined
expect(app._commands.get('foo')).to.be.ok
expect(app._commands.get('fooo')).to.be.undefined
expect(Object.keys(foo._options)).to.have.length(1)
})
})
Expand Down
4 changes: 2 additions & 2 deletions packages/plugin-common/src/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,8 +389,8 @@ export default function apply(ctx: Context, config: AdminConfig = {}) {
}

names = deduplicate(names)
const forbidden = names.filter(name => {
const command = ctx.app._commandMap[name]
const forbidden = names.filter((name) => {
const command = ctx.app._commands.get(name)
return command && command.config.authority >= session.user.authority
})
if (forbidden.length) return template('switch.forbidden', forbidden.join(', '))
Expand Down

0 comments on commit bc87290

Please sign in to comment.