diff --git a/mirai-console/backend/mirai-console/src/command/BuiltInCommands.kt b/mirai-console/backend/mirai-console/src/command/BuiltInCommands.kt index 060856f3a..b57af68d0 100644 --- a/mirai-console/backend/mirai-console/src/command/BuiltInCommands.kt +++ b/mirai-console/backend/mirai-console/src/command/BuiltInCommands.kt @@ -39,6 +39,7 @@ import net.mamoe.mirai.console.internal.pluginManagerImpl import net.mamoe.mirai.console.internal.util.runIgnoreException import net.mamoe.mirai.console.permission.Permission import net.mamoe.mirai.console.permission.Permission.Companion.parentsWithSelf +import net.mamoe.mirai.console.permission.PermissionId import net.mamoe.mirai.console.permission.PermissionService import net.mamoe.mirai.console.permission.PermissionService.Companion.cancel import net.mamoe.mirai.console.permission.PermissionService.Companion.findCorrespondingPermissionOrFail @@ -273,9 +274,94 @@ public object BuiltInCommands { @Description("查看所有权限列表") @SubCommand("listPermissions", "lp") public suspend fun CommandSender.listPermissions() { - sendMessage( - PermissionService.INSTANCE.getRegisteredPermissions() - .joinToString("\n") { it.id.toString() + " " + it.description }) + class PermTree( + val perm: Permission, + val sub: MutableList = mutableListOf(), + ) + + val rootView = PermTree(PermissionService.INSTANCE.rootPermission) + val mappings = mutableMapOf() + val permListSnapshot = PermissionService.INSTANCE.getRegisteredPermissions().toList() + + permListSnapshot.forEach { perm -> + mappings[perm.id] = PermTree(perm) + } + mappings[rootView.perm.id] = rootView + + permListSnapshot.forEach { perm -> + if (perm.id == rootView.perm.id) return@forEach + + val parentView = mappings[perm.parent.id] ?: error("Can't find parent of ${perm.id}: ${perm.parent.id}") + parentView.sub.add(perm.id) + } + + mappings.values.forEach { view -> + view.sub.sortWith { p1, p2 -> + val namespaceCompare = p1.namespace compareTo p2.namespace + if (namespaceCompare != 0) return@sortWith namespaceCompare + + if (p1.name == p2.name) return@sortWith 0 // ? + if (p1.name == "*") return@sortWith -1 + if (p2.name == "*") return@sortWith 1 + + return@sortWith p1.name compareTo p2.name + } + } + + //*:* + // | `- + // |- ..... + // | | `- + // | |- ...... + // | | | `- + // | | |- + val prefixed = 50 + + fun renderDepth(depth: Int, sb: AnsiMessageBuilder) { + repeat(depth) { sb.append(" | ") } + } + + fun render(depth: Int, view: PermTree, sb: AnsiMessageBuilder) { + kotlin.run { // render perm id + var doReset = false + val permId = view.perm.id + if (permId == rootView.perm.id || permId.name.endsWith("*")) { + doReset = true + sb.red() + } + sb.append(permId) + if (doReset) { + sb.reset() + } + } + + val linePrefixLen = + (depth * 3) + 1 + view.perm.id.let { it.name.length + it.namespace.length } + (if (depth == 0) 0 else 1) + val descFlatten = view.perm.description.replace("\r\n", "\n").replace("\r", "\n") + if (!descFlatten.contains('\n') && linePrefixLen < prefixed) { + if (descFlatten.isNotBlank()) { + repeat(prefixed - linePrefixLen) { sb.append(' ') } + sb.append(" ").append(descFlatten) + } + } else { + descFlatten.splitToSequence('\n').forEach { line -> + sb.append('\n') + renderDepth(depth, sb) + sb.append(" | `- ").append(line) + } + } + sb.append('\n') + + view.sub.forEach { sub -> + val subView = mappings[sub] ?: return@forEach + renderDepth(depth, sb) + sb.append(" |- ") + render(depth + 1, subView, sb) + } + } + sendAnsiMessage { + render(0, rootView, this) + } } }