diff --git a/.github/workflows/mkdocs-deploy.yml b/.github/workflows/vuepress-deploy.yml similarity index 69% rename from .github/workflows/mkdocs-deploy.yml rename to .github/workflows/vuepress-deploy.yml index 275d7498..68db8fd5 100644 --- a/.github/workflows/mkdocs-deploy.yml +++ b/.github/workflows/vuepress-deploy.yml @@ -1,4 +1,4 @@ -name: MkDocs Auto Deploy +name: VuePress Auto Deploy on: push: branches: @@ -13,15 +13,14 @@ jobs: uses: actions/checkout@v2 - name: Deploy docs to GitHub Pages - uses: mhausenblas/mkdocs-deploy-gh-pages@master + uses: jenkey2011/vuepress-deploy@master env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CUSTOM_DOMAIN: framework.zhamao.me - CONFIG_FILE: mkdocs.yml - EXTRA_PACKAGES: build-base + ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BUILD_SCRIPT: yarn && yarn docs:build + BUILD_DIR: docs/.vuepress/dist/ - name: Copy deployment to current folder run: | - cp -r "${GITHUB_WORKSPACE}/site" "./deploy" + cp -r "${GITHUB_WORKSPACE}/docs/.vuepress/dist" "./deploy" - name: Deploy to Zhamao Server uses: easingthemes/ssh-deploy@main env: diff --git a/.gitignore b/.gitignore index e028047b..507b4543 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,9 @@ composer.lock .zm_worker_*.pid # Git Hook 的相关锁文件 -cghooks.lock \ No newline at end of file +cghooks.lock + +# Vuepress 相关文件 +/node_modules/ +/docs/.vuepress/dist/ +package-lock.json \ No newline at end of file diff --git a/docs/.vuepress/components/ChatBox.vue b/docs/.vuepress/components/ChatBox.vue new file mode 100644 index 00000000..9bbac04d --- /dev/null +++ b/docs/.vuepress/components/ChatBox.vue @@ -0,0 +1,141 @@ + + + + + + + \ No newline at end of file diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js new file mode 100644 index 00000000..1fc952d0 --- /dev/null +++ b/docs/.vuepress/config.js @@ -0,0 +1,187 @@ +module.exports = { + title: '炸毛框架', + description: '一个聊天机器人 + Web 框架', + theme: 'antdocs', + markdown: { + lineNumbers: true + }, + head: [ + ['link', { rel: 'icon', href: '/logo_trans.png' }], + ['script', {}, ` + var _hmt = _hmt || []; +(function () { + var hm = document.createElement("script"); + hm.src = "https://hm.baidu.com/hm.js?f0f276cefa10aa31a20ae3815a50b795"; + var s = document.getElementsByTagName("script")[0]; + s.parentNode.insertBefore(hm, s); +})(); + `] + ], + themeConfig: { + repo: 'zhamao-robot/zhamao-framework', + logo: '/logo_trans.png', + docsDir: 'docs', + editLinks: true, + lastUpdated: '上次更新', + activeHeaderLinks: false, + nav: [ + { text: '指南', link: '/guide/' }, + { text: '事件和注解', link: '/event/' }, + { text: '组件', link: '/component/' }, + { text: '进阶', link: '/advanced/' }, + { text: 'FAQ', link: '/faq/' }, + { text: '更新日志', link: '/update/v2/' }, + { text: '炸毛框架 v1', link: 'https://docs-v1.zhamao.xin/' } + ], + sidebar: { + '/guide/': [ + { + title: '指南', + collapsable: false, + sidebarDepth: 1, + children: [ + '', + 'installation', + 'quickstart-robot', + 'quickstart-http', + 'onebot-choose', + 'basic-config', + 'write-module', + 'register-event', + 'upgrade', + 'errcode' + ] + } + ], + '/event/': [ + { + title: '事件和注解', + collapsable: false, + sidebarDepth: 1, + children: [ + '', + 'robot-annotations', + 'route-annotations', + 'framework-annotations', + 'middleware', + 'custom-annotations', + 'event-dispatcher' + ] + } + ], + '/component/': [ + '', + { + title: '聊天机器人组件', + collapsable: true, + sidebarDepth: 2, + children: [ + 'bot/robot-api-12', + 'bot/robot-api', + 'bot/cqcode', + 'bot/message-util', + 'bot/access-token', + 'bot/turing-api' + ] + }, + { + title: '存储组件', + collapsable: true, + sidebarDepth: 2, + children: [ + 'store/light-cache', + 'store/mysql', + 'store/mysql-db', + 'store/redis', + 'store/atomics', + 'store/spin-lock', + 'store/data-provider' + ] + }, + { + title: '通用组件', + collapsable: true, + sidebarDepth: 2, + children: [ + 'common/context', + 'common/coroutine-pool', + 'common/singleton-trait', + 'common/zmutil', + 'common/global-functions', + 'common/console', + 'common/task-worker', + 'common/remote-terminal', + 'common/event-tracer' + ] + }, + { + title: 'HTTP 组件', + collapsable: true, + sidebarDepth: 2, + children: [ + 'http/zmrequest', + 'http/route-manager' + ] + }, + { + title: '模块/插件管理', + collapsable: true, + sidebarDepth: 2, + children: [ + 'module/module-pack', + 'module/module-unpack' + ] + } + ], + '/advanced/': [ + '', + { + title: '框架高级开发', + collapsable: true, + sidebarDepth: 2, + children: [ + 'framework-structure', + 'custom-start', + 'manually-install', + 'inside-class', + 'multi-process', + 'task-worker' + ] + }, + { + title: '开发实战教程', + collapsable: true, + sidebarDepth: 2, + children: [ + 'connect-ws-client', + 'example/admin', + 'example/integrate-qingyunke-chatbot', + 'example/weather-bot' + ] + }, + ], + '/faq/': [ + '', + 'to-v2', + 'usual-question', + 'address-already-in-use', + 'display-deadlock', + 'light-cache-wrong', + 'wait-message-cqbefore' + ], + '/update/': [ + { + title: '更新日志', + collapsable: true, + sidebarDepth: 0, + children: [ + 'v2', + 'v1', + 'build-update' + ] + }, + 'config' + ] + } + } +} \ No newline at end of file diff --git a/docs/.vuepress/public/CNAME b/docs/.vuepress/public/CNAME new file mode 100644 index 00000000..3a8df7b0 --- /dev/null +++ b/docs/.vuepress/public/CNAME @@ -0,0 +1 @@ +framework.zhamao.me \ No newline at end of file diff --git a/docs/.vuepress/public/logo_trans.png b/docs/.vuepress/public/logo_trans.png new file mode 100644 index 00000000..f53efa4e Binary files /dev/null and b/docs/.vuepress/public/logo_trans.png differ diff --git a/docs/assets/logos.png b/docs/.vuepress/public/logos.png similarity index 100% rename from docs/assets/logos.png rename to docs/.vuepress/public/logos.png diff --git a/docs/.vuepress/styles/palette.less b/docs/.vuepress/styles/palette.less new file mode 100644 index 00000000..bc2c7a3a --- /dev/null +++ b/docs/.vuepress/styles/palette.less @@ -0,0 +1 @@ +@accentColor: #3665aa; \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..d5db3d43 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,18 @@ +--- +home: true +heroImage: ./logo_trans.png +actionBtn: + text: 快速上手 + link: /guide/ + type: primary + size: large +features: +- title: 高性能 + details: 基于 PHP 的 Swoole 高性能扩展,利用 WebSocket 进行与 OneBot 协议兼容的聊天机器人软件的通信,还有数据库连接池、内存缓存、多任务进程等特色,大幅增强性能。 +- title: 易于开发 + details: 所有功能采用模块化设计,除特殊情况外几乎所有功能都不需要修改框架内任意代码,框架采用灵活的注解进行各类事件绑定,同时支持下断点调试。 +- title: 接口直观 + details: 支持命令、普通文本、正则匹配、自然语言处理等多种对话解析方式,利用协程巧妙实现了直观的交互式会话模式,同时支持多种富文本的处理。 +footer: | + Apache-2.0 Licensed  |  Copyright © 2019-2022 Zhamao Developer Team  |  沪ICP备2021010446号-1 +--- \ No newline at end of file diff --git a/docs/advanced/README.md b/docs/advanced/README.md new file mode 100644 index 00000000..d680486d --- /dev/null +++ b/docs/advanced/README.md @@ -0,0 +1,4 @@ +# 进阶开发 +在本章,下面的部分将详细说明一些具体的案例和自定义框架的操作。 + +> 更多进阶教程敬请期待....(或者你可以选择提 Issue 到框架 GitHub,有需求就写入文档) diff --git a/docs/advanced/connect-ws-client.md b/docs/advanced/connect-ws-client.md index ea08148d..49c04a48 100644 --- a/docs/advanced/connect-ws-client.md +++ b/docs/advanced/connect-ws-client.md @@ -11,9 +11,11 @@ 以上两种方式,`Header` 方式比 `GET` 方式优先级要高,如果两者均没有指定,框架会将此连接当作 `default` 类型接入。 -!!! note "提示" +::: tip 提示 - 对于对接 OneBot 标准的机器人客户端,只要符合 OneBot 标准,即 `X-Client-Role` 会自动带上 `universal`、`qq` 等字样,就会自动标记为 `qq` 类型。 +对于对接 OneBot 标准的机器人客户端,只要符合 OneBot 标准,即 `X-Client-Role` 会自动带上 `universal`、`qq` 等字样,就会自动标记为 `qq` 类型。 + +::: ## 逻辑编写 @@ -140,4 +142,3 @@ public function onMessage() { ```php server()->close($conn->getFd()); ``` - diff --git a/docs/advanced/custom-start.md b/docs/advanced/custom-start.md index 68bee88d..b3d4f096 100644 --- a/docs/advanced/custom-start.md +++ b/docs/advanced/custom-start.md @@ -5,8 +5,10 @@ 从前面的几章中,我们了解到框架有多种下载到本地的方式。 - Composer 依赖模式 -- Starter 从模板创建模式 +- Starter 从模板创建模式(等同于 Composer 模式) - 源码模式 +- Phar Composer 依赖模式 +- Phar 源码模式 ### Composer 依赖模式 @@ -131,4 +133,4 @@ vendor/bin/start simple-http-server your-web-dir/ --host=0.0.0.0 --port=8080 命令:`vendor/bin/start systemd:generate` -注意,systemd 启动的守护进程模式和使用参数 `--daemon` 不一样,请勿同时混用,直接使用上述命令生成的配置文件即可正常使用! \ No newline at end of file +注意,systemd 启动的守护进程模式和使用参数 `--daemon` 不一样,请勿同时混用,直接使用上述命令生成的配置文件即可正常使用! diff --git a/docs/advanced/example/admin.md b/docs/advanced/example/admin.md index a2eaf2e0..d67cd913 100644 --- a/docs/advanced/example/admin.md +++ b/docs/advanced/example/admin.md @@ -87,14 +87,13 @@ class AdminMiddleware implements MiddlewareInterface */ ``` - -^ 假设我是管理员 -) 禁言 1234567 600 -( 禁言成功! -^ 假设我不在管理员名单里 -) 禁言 1234567 900 -^ 机器人没有回复,因为中间件返回了 false,不继续执行 - + 而这时候有朋友又要问了,如果我有一系列管理员命令,假设都在一个叫 `AdminFunc.php` 的模块类里,我是不是还得一个一个地给注解事件写 `@Middleware("admin")` 呢?当然不需要!如果你这个类所有的注解事件都是机器人的聊天事件(`@CQCommand`,`@CQMessage`)的话,可以直接给类注解这个中间件,效果等同于给每一个函数写一次中间件注解。 @@ -120,112 +119,111 @@ class AdminFunc 上面我们讲到了,中间件里面使用了 `LightCache` 轻量缓存来储存临时的管理员列表,那么我们将这部分的代码完善吧! -=== "src/Module/Example/AdminFunc.php" +**src/Module/Example/AdminFunc.php** - ```php - getNextArg("请输入禁言的人或at他"); + $r2 = ctx()->getFullArg("请输入禁言的时间(秒)"); + $cq = CQ::getCQ($r1); + if ($cq !== null) { + if ($cq["type"] != "at") return "请at或者输入正确的QQ号!"; + $r1 = $cq["params"]["qq"]; + } + // 群内禁言用户 + ctx()->getRobot()->setGroupBan(ctx()->getGroupId(), $r1, $r2); + return "禁言成功!"; + } /** - * Class AdminFunc - * @package Module\Example + * @CQCommand(match="解除禁言",message_type="group") + */ + public function unbanSomeone() { + $r1 = ctx()->getNextArg("请输入禁言的人或at他"); + $cq = CQ::getCQ($r1); + if ($cq !== null) { + if ($cq["type"] != "at") return "请at或者输入正确的QQ号!"; + $r1 = $cq["params"]["qq"]; + } + // 群内禁言用户 + ctx()->getRobot()->setGroupBan(ctx()->getGroupId(), $r1, 0); + return "解除禁言成功!"; + } +} +``` + +**src/Module/Example/AdminManager.php** + +```php +getNextArg("请输入禁言的人或at他"); - $r2 = ctx()->getFullArg("请输入禁言的时间(秒)"); - $cq = CQ::getCQ($r1); - if ($cq !== null) { - if ($cq["type"] != "at") return "请at或者输入正确的QQ号!"; - $r1 = $cq["params"]["qq"]; - } - // 群内禁言用户 - ctx()->getRobot()->setGroupBan(ctx()->getGroupId(), $r1, $r2); - return "禁言成功!"; - } - - /** - * @CQCommand(match="解除禁言",message_type="group") - */ - public function unbanSomeone() { - $r1 = ctx()->getNextArg("请输入禁言的人或at他"); - $cq = CQ::getCQ($r1); - if ($cq !== null) { - if ($cq["type"] != "at") return "请at或者输入正确的QQ号!"; - $r1 = $cq["params"]["qq"]; - } - // 群内禁言用户 - ctx()->getRobot()->setGroupBan(ctx()->getGroupId(), $r1, 0); - return "解除禁言成功!"; - } + public function addAdmin() { //只有管理员才能添加管理员 + $qq = ctx()->getNextArg("请输入要添加管理员的QQ(qq号码,不可at)"); + SpinLock::lock("admin_list"); //如果是多进程模式的话需要加锁 + $ls = LightCache::get("admin_list"); + if (!in_array($qq, $ls)) $ls[] = $qq; + LightCache::set("admin_list", $ls, -2); + SpinLock::unlock("admin_list"); //如果是多进程模式的话需要加锁 + return "成功添加 $qq 到管理员列表!"; } - ``` - -=== "src/Module/Example/AdminManager.php" - - ```php - getNextArg("请输入要添加管理员的QQ(qq号码,不可at)"); - SpinLock::lock("admin_list"); //如果是多进程模式的话需要加锁 - $ls = LightCache::get("admin_list"); - if (!in_array($qq, $ls)) $ls[] = $qq; - LightCache::set("admin_list", $ls, -2); - SpinLock::unlock("admin_list"); //如果是多进程模式的话需要加锁 - return "成功添加 $qq 到管理员列表!"; - } - } - ``` - - -^ 现在我是 123456 -) 禁言 13579 60 -( 禁言成功! -) 解除禁言 13579 -( 解除禁言成功! -) 添加管理员 98765 -( 成功添加 98765 到管理员列表! -^ 现在我是98765 -) 禁言 13579 -( 请输入禁言的时间(秒) -) 120 -( 禁言成功! - +} +``` + diff --git a/docs/advanced/example/integrate-qingyunke-chatbot.md b/docs/advanced/example/integrate-qingyunke-chatbot.md index cd193744..18d23440 100644 --- a/docs/advanced/example/integrate-qingyunke-chatbot.md +++ b/docs/advanced/example/integrate-qingyunke-chatbot.md @@ -3,8 +3,8 @@ 作为一个群聊机器人,懂得聊天会让机器人增色不少,在大数据和 AI 热潮下,不少厂商都研发了自己的智能聊天 API,例如图灵机器人、腾讯智能闲聊等,大厂开发的 API 自然有着他人无可比拟的健壮性和可靠性,但是随之而来不菲的价格显然并不适合大众开发者。这时候一个免费、可用的智能聊天 API 便非常重要了,其中,青云客是少有的完全免费、无需注册的智能聊天 API,提供了包括智能聊天、歌词、天气查询、笑话等多种有用功能,且接入简单,非常适合新手开发者尝试。 ## 结果演示 -![圖片](https://user-images.githubusercontent.com/31698606/158875192-108698a3-b54e-4fc0-889a-0829ca328b13.png) +![圖片](https://user-images.githubusercontent.com/31698606/158875192-108698a3-b54e-4fc0-889a-0829ca328b13.png) ## 阅读接入指南 @@ -83,6 +83,3 @@ public function changeAt(): bool return true; } ``` - - - diff --git a/docs/advanced/example/weather-bot.md b/docs/advanced/example/weather-bot.md index f31cd5ca..1758c5fd 100644 --- a/docs/advanced/example/weather-bot.md +++ b/docs/advanced/example/weather-bot.md @@ -3,20 +3,24 @@ 本文将基于 [`jieba-php`](https://github.com/fukuball/jieba-php) 中文分词库以及 [魅族天气 API](https://github.com/shichunlei/-Api/blob/master/MeizuWeather.md) 开发一个天气查询机器人。 ## 结果演示 + ![圖片](https://user-images.githubusercontent.com/31698606/159122016-61ba9696-5786-4561-b371-827d9f1d01aa.png) 尾部的随机表情并非本教程的一部分。 ## 逻辑编写 -[`jieba-php`](https://github.com/fukuball/jieba-php) 是目前比较好用的中文分词库,虽然最近的维护并不活跃,但已足够我们的需求: +[jieba-php](https://github.com/fukuball/jieba-php) 是目前比较好用的中文分词库,虽然最近的维护并不活跃,但已足够我们的需求: + ```shell composer require fukuball/jieba-php:dev-master ``` 以下代码使用了本文作者自行编写的天气查询库,需要进行引入: + ```shell composer require sunxyw/weather ``` + 您也可以将以下代码自行改写为直接调用魅族天气 API,详情请参阅[魅族天气 API 文档](https://github.com/shichunlei/-Api/blob/master/MeizuWeather.md)。 ```php @@ -107,6 +111,3 @@ EOF; } } ``` - - - diff --git a/docs/advanced/framework-structure.md b/docs/advanced/framework-structure.md index e3259b69..10ebac05 100644 --- a/docs/advanced/framework-structure.md +++ b/docs/advanced/framework-structure.md @@ -3,4 +3,3 @@ ## 框架运行总结构图 ![](https://static.zhamao.me/images/docs/a23d8a952cf9c88d395888d220605a4f.png) - diff --git a/docs/advanced/index.md b/docs/advanced/index.md deleted file mode 100644 index 06229b11..00000000 --- a/docs/advanced/index.md +++ /dev/null @@ -1,9 +0,0 @@ -# 进阶开发 -在本章,下面的部分将详细说明一些具体的案例和自定义框架的操作。 - -- 如何自定义修改框架本身?- [框架启动方式](/advanced/custom-start/) -- 如何接入一个自己的 WebSocket 客户端?- [接入 WebSocket 客户端](/advanced/connect-ws-client/) -- 框架到底是怎么工作的?- [框架结构剖析](/advanced/framework-structure/) - -> 更多进阶教程敬请期待....(或者你可以选择提 Issue 到框架 GitHub,有需求就写入文档) - diff --git a/docs/advanced/inside-class.md b/docs/advanced/inside-class.md index fb300250..3cdaa84b 100644 --- a/docs/advanced/inside-class.md +++ b/docs/advanced/inside-class.md @@ -65,6 +65,3 @@ $obj->match ==> [ ] */ ``` - - - diff --git a/docs/advanced/manually-install.md b/docs/advanced/manually-install.md index c53a49a5..9d9abdce 100644 --- a/docs/advanced/manually-install.md +++ b/docs/advanced/manually-install.md @@ -1,3 +1,3 @@ # 手动部署环境教程 -TODO: 还没写 \ No newline at end of file +TODO: 还没写 diff --git a/docs/advanced/multi-process.md b/docs/advanced/multi-process.md index 3238c028..76d13477 100644 --- a/docs/advanced/multi-process.md +++ b/docs/advanced/multi-process.md @@ -67,4 +67,3 @@ PHP 也是如此,框架的多进程又是怎么一回事呢?为什么要采 对于 WorkerCache 来说,其实是比较特殊的进程间通信。具体来说就是,WorkerCache 的原理就是将变量指定的存到一个进程中,如果是本进程读写的话直接相当于改一下全局变量,如果是其他进程读写的话,则依靠进程间通信。 所以缺点也显而易见,如果使用过程中不是命中了 WorkerCache 存储所在的进程的话,则一直会使用进程间通信,影响一定的效率。 - diff --git a/docs/advanced/php-env.md b/docs/advanced/php-env.md new file mode 100644 index 00000000..85d00fa4 --- /dev/null +++ b/docs/advanced/php-env.md @@ -0,0 +1,3 @@ +# PHP 环境高级部署方式 + +TODO: 留个坑。 diff --git a/docs/advanced/task-worker.md b/docs/advanced/task-worker.md index fda8ffcb..620d9674 100644 --- a/docs/advanced/task-worker.md +++ b/docs/advanced/task-worker.md @@ -1,4 +1,3 @@ # 使用 TaskWorker 进程处理密集运算 -> 新开个坑,有时间补上。(__填坑标记__) - +> TODO: 新开个坑,有时间补上。(__填坑标记__) diff --git a/docs/assets/css/extra.css b/docs/assets/css/extra.css deleted file mode 100644 index e6f58e08..00000000 --- a/docs/assets/css/extra.css +++ /dev/null @@ -1,103 +0,0 @@ - -.md-header-nav__button.md-logo { - padding: .2rem; - margin: .2rem; -} - -.md-header-nav__button.md-logo img, .md-header-nav__button.md-logo svg { - width: 1.6rem; - height: 1.6rem; -} - -.doc-chat-container { - border-radius: 6px; - width: 100%; - min-height: 30px; - /*noinspection CssUnresolvedCustomProperty*/ - background-color: var(--md-code-bg-color); - padding: 12px; - margin-right: auto; - margin-left: auto; - box-shadow: 0 3px 1px -2px rgba(0,0,0,.2), 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12); -} - -.doc-chat-row { - margin: 0; - display: flex; - flex-wrap: wrap; - flex: 1 1 auto; - justify-content: flex-end; -} - -.doc-chat-banner { - justify-content: center; - background: rgba(0,0,0,0.1); - width: max-content; - margin: 8px auto; - padding: 4px 14px; - border-radius: 8px; - color: gray; - font-size: 14px; -} - -.doc-chat-row-robot { - justify-content: flex-start !important; -} - -.doc-chat-box { - color: #000000de; - position: relative; - width: fit-content; - max-width: 55%; - border-radius: .5rem; - padding: .4rem .6rem; - margin: .4rem .8rem; - background-color: #fff; - line-height: 1.5; - font-size: 16px; - outline: none; - overflow-wrap: break-word; - white-space: normal; - box-shadow: 0 2px 12px 0 rgba(0,0,0,.1); -} - -.doc-chat-box:after { - content: ""; - position: absolute; - right: auto; - top: 0; - width: 8px; - height: 12px; - color: #fff; - border: 0 solid transparent; - border-bottom: 7px solid; - border-radius: 0 0 8px 0; - left: calc(100% - 4px); - box-sizing: inherit; -} - -.doc-chat-box-robot:after { - content: ""; - position: absolute; - right: calc(100% - 4px); - top: 0; - width: 8px; - height: 12px; - color: #fff; - border: 0 solid transparent; - border-bottom: 7px solid; - border-radius: 0 0 0 8px; - left: auto; - box-sizing: inherit; -} - -.doc-chat-avatar { - background-color: aquamarine; - width: 36px !important; - height: 36px !important; - border-radius: 18px; -} - -.md-typeset .admonition, .md-typeset details { - font-size: .72rem; -} \ No newline at end of file diff --git a/docs/assets/css/library/default.min.css b/docs/assets/css/library/default.min.css deleted file mode 100644 index 8fc9a157..00000000 --- a/docs/assets/css/library/default.min.css +++ /dev/null @@ -1 +0,0 @@ -.hljs{display:block;overflow-x:auto;padding:.5em;background:#f0f0f0}.hljs,.hljs-subst{color:#444}.hljs-comment{color:#888}.hljs-attribute,.hljs-doctag,.hljs-keyword,.hljs-meta-keyword,.hljs-name,.hljs-selector-tag{font-weight:700}.hljs-deletion,.hljs-number,.hljs-quote,.hljs-selector-class,.hljs-selector-id,.hljs-string,.hljs-template-tag,.hljs-type{color:#800}.hljs-section,.hljs-title{color:#800;font-weight:700}.hljs-link,.hljs-regexp,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-symbol,.hljs-template-variable,.hljs-variable{color:#bc6060}.hljs-literal{color:#78a960}.hljs-addition,.hljs-built_in,.hljs-bullet,.hljs-code{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700} \ No newline at end of file diff --git a/docs/assets/favicon.png b/docs/assets/favicon.png deleted file mode 100644 index fa2ccbd0..00000000 Binary files a/docs/assets/favicon.png and /dev/null differ diff --git a/docs/component/README.md b/docs/component/README.md new file mode 100644 index 00000000..6d97ddf9 --- /dev/null +++ b/docs/component/README.md @@ -0,0 +1,3 @@ +# 框架组件 + +这里列举了框架内的你可能会用到的常用组件。 diff --git a/docs/component/bot/access-token.md b/docs/component/bot/access-token.md index 3e4b4b4e..92638df3 100644 --- a/docs/component/bot/access-token.md +++ b/docs/component/bot/access-token.md @@ -1,4 +1,4 @@ -# Token 验证 +# 接入安全验证 - Token 为了保障安全,框架支持给接入的 WebSocket 连接验证 Token,如果不设置 Token 同时又将框架的端口暴露在公网将会非常危险。 @@ -27,9 +27,9 @@ go-cqhttp 的配置段: -```hjson - // 访问密钥, 强烈推荐在公网的服务器设置 - access_token: "emhhbWFvLXJvYm90" +``` +// 访问密钥, 强烈推荐在公网的服务器设置 +access_token: "emhhbWFvLXJvYm90" ``` 框架的配置文件配置段: @@ -56,4 +56,4 @@ $config["access_token"] = function($token){ ## 自定义验证(open 事件) -当然,这里设置了自定义方式,其实你也可以在下一层的 `@OnOpenEvent` 注解事件中进行自定义内容和判断,具体见 `@OnOpenEvent` 的相关章节。 \ No newline at end of file +当然,这里设置了自定义方式,其实你也可以在下一层的 `@OnOpenEvent` 注解事件中进行自定义内容和判断,具体见 `@OnOpenEvent` 的相关章节。 diff --git a/docs/component/bot/cqcode.md b/docs/component/bot/cqcode.md index e32c113c..4989ba1f 100644 --- a/docs/component/bot/cqcode.md +++ b/docs/component/bot/cqcode.md @@ -1,4 +1,4 @@ -# CQ 码(多媒体消息) +# 多媒体消息 - CQ码 消息中的多媒体内容使用 CQ 码来表示,形如 `[CQ:face,id=178]`。其中,`[CQ:]` 是固定格式;`face` 是「功能名」,除了 `face` 还有许多不同的功能名;`id=178` 是「参数」,某些功能不需要参数,而另一些需要多个参数,当有多个参数时,参数间使用逗号分隔。 @@ -14,11 +14,13 @@ 更多 CQ 码功能请参考 [消息段类型](https://github.com/howmanybots/onebot/blob/master/v11/specs/message/segment.md)。 -!!! warning "注意" +::: warning 注意 - CQ 码中不应有多余的空格,例如不应该使用 `[CQ:face, id=178]`。 +CQ 码中不应有多余的空格,例如不应该使用 `[CQ:face, id=178]`。 - CQ 码的参数值可以包含空格、换行、除 `[],&` 之外的特殊符号等。在解析时,应直接取 `[CQ:` 后、第一个 `,` 或 `]` 前的部分为功能名,第一个 `,` 之后到 `]` 之间的部分为参数,按 `,` 分割后,每个部分第一个 `=` 前的内容为参数名,之后的部分为参数值。例如 `[CQ:share,title=标题中有=等号,url=http://baidu.com]` 中,功能名为 `share`,`title` 参数值为 `标题中有=等号`,`url` 参数值为 `http://baidu.com`。 +CQ 码的参数值可以包含空格、换行、除 `[],&` 之外的特殊符号等。在解析时,应直接取 `[CQ:` 后、第一个 `,` 或 `]` 前的部分为功能名,第一个 `,` 之后到 `]` 之间的部分为参数,按 `,` 分割后,每个部分第一个 `=` 前的内容为参数名,之后的部分为参数值。例如 `[CQ:share,title=标题中有=等号,url=http://baidu.com]` 中,功能名为 `share`,`title` 参数值为 `标题中有=等号`,`url` 参数值为 `http://baidu.com`。 + +::: ## 转义 @@ -69,12 +71,12 @@ class Hello { } ``` -效果 +效果: - -) 发送图片 -[ https://zhamao.xin/file/hello.jpg - + ## CQ 码操作 @@ -191,15 +193,17 @@ public function faceTest() { } ``` - -) 打盹 -( 正在打盹... -[ https://docs-v1.zhamao.xin/face/8.gif - + -!!! note "提示" - 对于不断更新的 QQ 版本下,可能会持续扩充新的 QQ 表情,如果上表没有新的表情的话,也可以使用消息接收的方式,让机器人收到表情后解析出来对应的 id 然后再发送。 +::: tip 提示 +对于不断更新的 QQ 版本下,可能会持续扩充新的 QQ 表情,如果上表没有新的表情的话,也可以使用消息接收的方式,让机器人收到表情后解析出来对应的 id 然后再发送。 + +::: ### CQ::image() - 发送图片 @@ -221,7 +225,11 @@ public function faceTest() { - 绝对路径,例如 `file:///root/imagetest/1.png`,格式使用 [`file` URI](https://tools.ietf.org/html/rfc8089) - 网络 URL,例如 `http://i1.piimg.com/567571/fdd6e7b6d93f1ef0.jpg` -- Base64 编码,例如 `base64://iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAIAAADJt1n/AAAAKElEQVQ4EWPk5+RmIBcwkasRpG9UM4mhNxpgowFGMARGEwnBIEJVAAAdBgBNAZf+QAAAAABJRU5ErkJggg==` +- Base64 编码,例如 + +``` +base64://iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAIAAADJt1n/AAAAKElEQVQ4EWPk5+RmIBcwkasRpG9UM4mhNxpgowFGMARGEwnBIEJVAAAdBgBNAZf+QAAAAABJRU5ErkJggg== +``` ### CQ::record() - 发送语音 @@ -250,10 +258,10 @@ public function say() { } ``` - -) 说你好 -( [语音消息,点击收听] 2'' ))) - + > 此 CQ 码只能用于单独一条文本消息中,如果混有其他字符串,则会吞掉其他字符串内容。 @@ -274,10 +282,10 @@ public function atTest() { } ``` - -) at测试 -( @鲸鱼 你好啊! - + ### CQ::video() - 发送短视频 @@ -346,9 +354,11 @@ public function atTest() { 匿名发消息。需要在允许匿名发消息的群里发。 -!!! tip "提示" +::: tip 提示 - 当收到匿名消息时,需要通过 [消息事件的群消息](https://github.com/howmanybots/onebot/blob/master/v11/specs/event/message.md#群消息) 的 `anonymous` 字段判断。 +当收到匿名消息时,需要通过 [消息事件的群消息](https://github.com/howmanybots/onebot/blob/master/v11/specs/event/message.md#群消息) 的 `anonymous` 字段判断。 + +::: 定义:`CQ::anonymous($ignore = 1)` @@ -515,9 +525,11 @@ public function xmlTest() { 其中 `$resid` 是面向 go-cqhttp 扩展的参数,默认不填为 0,走小程序通道,填了走富文本通道发送。 -!!! tip "提示" +::: tip 提示 - 因为某些众所周知的原因,XML 和 JSON 的返回不提供实例,有兴趣的可以自行研究如何编写,文档不含任何相关教程。 +因为某些众所周知的原因,XML 和 JSON 的返回不提供实例,有兴趣的可以自行研究如何编写,文档不含任何相关教程。 + +::: ### CQ::_custom() - 扩展自定义 CQ 码 @@ -536,4 +548,3 @@ public function xmlTest() { CQ::_custom("at",["qq" => "123456","qwe" => "asd"]); // 返回:[CQ:at,qq=123456,qwe=asd] ``` - diff --git a/docs/component/bot/message-util.md b/docs/component/bot/message-util.md index 5ba3dabe..518f9a1f 100644 --- a/docs/component/bot/message-util.md +++ b/docs/component/bot/message-util.md @@ -1,4 +1,4 @@ -# MessageUtil 消息处理工具类 +# 消息处理工具类 - MessageUtil 类定义:`\ZM\Utils\MessageUtil` @@ -81,9 +81,11 @@ MessageUtil::isAtMe("[CQ:at,qq=123456789]另一个朋友你好","123456"); // fa 返回:数组,切分后的。 -!!! tip "为什么不直接使用 explode 呢" +::: tip 为什么不直接使用 explode 呢 - 因为 `explode()` 只会简单粗暴的切割字符串,假设用户输入的消息中两个词中间有多个空格,则会有空的词出现。例如 `你好 我是一个长空格`。此函数会将多个空格当作一个空格来对待。 +因为 `explode()` 只会简单粗暴的切割字符串,假设用户输入的消息中两个词中间有多个空格,则会有空的词出现。例如 `你好 我是一个长空格`。此函数会将多个空格当作一个空格来对待。 + +::: ```php MessageUtil::splitCommand("你好 我是傻瓜\n我是傻瓜二号"); // ["你好","我是傻瓜","我是傻瓜二号"] @@ -123,10 +125,10 @@ public function onStart() { } ``` - -) 炸毛不聪明 -( 其实还是很聪明的! - + ### strToArray() @@ -176,4 +178,4 @@ $arr = \ZM\Utils\MessageUtil::strToArray($str); // 我们使用上边的 $arr 作为传入值。 $new_str = \ZM\Utils\MessageUtil::arrayToStr($arr); // 结果:"你好啊,[CQ:at,qq=123]" -``` \ No newline at end of file +``` diff --git a/docs/component/bot/robot-api-12.md b/docs/component/bot/robot-api-12.md index ac45f300..de1e1fef 100644 --- a/docs/component/bot/robot-api-12.md +++ b/docs/component/bot/robot-api-12.md @@ -1,9 +1,11 @@ -# 机器人 API(OneBotV12)(待发布) +# 机器人动作 - V12 -!!! tip "提示" +::: tip 提示 - 目前由于 OneBot 12 标准还没有定稿,处于草案阶段,故框架暂不更新。 - - 在未来升级到 OneBot 12 标准后,框架会提供转换及兼容措施以及 12 版本的 API 方法。 +目前由于 OneBot 12 标准还没有定稿,处于草案阶段,故框架暂不更新。 -见 [机器人动作(OneBot 11)](../robot-api)。 \ No newline at end of file +在未来升级到 OneBot 12 标准后,框架会提供转换及兼容措施以及 12 版本的 API 方法。 + +::: + +见 [机器人动作(OneBot 11)](./robot-api.html)。 \ No newline at end of file diff --git a/docs/component/bot/robot-api.md b/docs/component/bot/robot-api.md index 194456cf..f2aea0b9 100644 --- a/docs/component/bot/robot-api.md +++ b/docs/component/bot/robot-api.md @@ -1,4 +1,4 @@ -# 机器人 API(OneBotV11) +# 机器人动作 - V11 OneBotV11 类是封装好的 OneBot 标准的 API 接口调用类,可以在机器人连接后通过连接或者机器人 QQ 号获取对象并调用接口(如发送群消息、获取群列表等操作)。 @@ -111,8 +111,9 @@ $obj = $bot->sendGroupMsg("234567", "你好"); echo json_encode($obj, 128|256); ``` +输出结果 + ```json -// 输出结果 { "status": "ok", "retcode": 0, @@ -161,21 +162,18 @@ vardump($result["retcode"]); //如果成功撤回,输出 int(0) | ------------ | -------------- | ------- | | `message_id` | number (int32) | 消息 ID | -例子 +代码 -=== "代码" +```php +$bot = OneBotV11::get(123456); // 123456是你的机器人QQ +$bot->sendPrivateMsg("627577391", "你好啊!你好你好!"); +``` - ```php - $bot = OneBotV11::get(123456); // 123456是你的机器人QQ - $bot->sendPrivateMsg("627577391", "你好啊!你好你好!"); - ``` - -=== "效果" - - - ( 你好啊!你好你好! - +效果 + ### sendGroupMsg() @@ -621,10 +619,11 @@ vardump($result["retcode"]); //如果成功撤回,输出 int(0) 获取 Cookies。 -!!! warning "注意" +::: warning 注意 - 目前开源的 mirai 为底层的机器人客户端均不支持获取 Cookies 和 CSRF Token,包括 go-cqhttp。 +目前开源的 mirai 为底层的机器人客户端均不支持获取 Cookies 和 CSRF Token,包括 go-cqhttp。 +::: 参数 @@ -780,9 +779,11 @@ vardump($result["retcode"]); //如果成功撤回,输出 int(0) 唯一一个参数做保留,用于选择不同客户端,目前仅支持 `go-cqhttp`,所以缺省也默认为 `go-cqhttp`。 -!!! warning "注意" +::: warning 注意 - 由于不同版本的扩展 API 变化可能会很大,改动较多,炸毛框架不会将对应扩展方法写入文档,具体调用情况可根据 IDE 自动补全中的文档或对应类的注释查看。 +由于不同版本的扩展 API 变化可能会很大,改动较多,炸毛框架不会将对应扩展方法写入文档,具体调用情况可根据 IDE 自动补全中的文档或对应类的注释查看。 + +::: ### callExtendedAPI() (扩充 API) @@ -805,4 +806,3 @@ $result = $bot->callExtendedAPI("get_group_root_files", ["group_id" => 123456]); var_dump($result["data"]); // 输出群文件列表 ``` - diff --git a/docs/component/bot/turing-api.md b/docs/component/bot/turing-api.md index 0b637054..15b6d33e 100644 --- a/docs/component/bot/turing-api.md +++ b/docs/component/bot/turing-api.md @@ -1,4 +1,4 @@ -# 图灵机器人 API(TuringAPI) +# 图灵聊天 - TuringAPI 类定义:`\ZM\API\TuringAPI` @@ -60,27 +60,27 @@ class Hello { 如上述代码,我们将申请的 apikey 填入变量 `$api` 中,启动机器人即可使用,以下是实测消息(我用自己申请的 key 做测试回复的消息)。 - -) 你咋了 -( 我没事哦,谢谢您的关心。 -) 上海天气 -( 上海:周一 03月29日,小雨 东南风转东风,最低气温14度,最高气温24度。 -^ 切换为群内 -) 机器人 -( 我在!有什么事吗? -) 你叫啥 -( 我的名字叫炸毛,认识你很高兴呢! - + 在默认示例模块中的例子是直接可以拿来用的,这段代码同时做了对 at 的处理、以及兼容用户自定义写的其他命令的方式,下面是默认模块填好 apikey 后可以用的各种方式提问: - -^ 切换为群内 -) 我是一条普通消息,这条机器人不会回复我 -) @机器人 你叫啥 -( 我是聪明可爱的炸毛,认识你很高兴。 -) 机器人 -( 我在!有什么事吗? -) 一言 -( 多少事,从来急,天地转,光阴迫,一万年太久,只争朝夕。 - \ No newline at end of file + diff --git a/docs/component/common/console.md b/docs/component/common/console.md index 6d6d871c..53819176 100644 --- a/docs/component/common/console.md +++ b/docs/component/common/console.md @@ -52,10 +52,11 @@ vendor/bin/start server --log-debug # 以 debug 等级启动框架 输出 warning 级别的 log。 -!!! warning 注意 +::: warning 注意 - 框架内出现的用户态异常,比如无法发送 API、无法连接数据库等错误,都是 warning 错误,不会导致框架崩溃或功能错误的异常情况建议都使用 warning 输出而不是 error。 +框架内出现的用户态异常,比如无法发送 API、无法连接数据库等错误,都是 warning 错误,不会导致框架崩溃或功能错误的异常情况建议都使用 warning 输出而不是 error。 +::: ### Console::info() @@ -100,9 +101,11 @@ $str = Console::setColor("I am gold color.", "gold"); 炸毛框架支持从终端输入命令来进行一些操作,例如重启框架、停止框架、执行函数等。 -!!! warning 注意 +::: warning 注意 - 在 Docker、systemd、daemon 状态下启动的框架会自动关闭终端等待输入,交互不可用。 +在 Docker、systemd、daemon 状态下启动的框架会自动关闭终端等待输入,交互不可用。 + +::: ### reload @@ -174,4 +177,3 @@ vendor/bin/start server --log-theme={主题名} vendor/bin/start server --log-theme=white-term # 如果用的是白色终端,这个主题更友好 vendor/bin/start server --log-theme=no-color # 如果不想让 log 带有任何颜色,使用无色主题 ``` - diff --git a/docs/component/common/context.md b/docs/component/common/context.md index 65c719bb..4c746c9a 100644 --- a/docs/component/common/context.md +++ b/docs/component/common/context.md @@ -40,31 +40,33 @@ public function hello() { 可以使用的事件:所有 **getFrame()** 可以使用的,`@OnOpenEvent()`,`@OnCloseEvent()` -!!! tip "提示" +::: tip 提示 - 值得注意的是,由于机器人客户端和炸毛框架的连接是通过 WebSocket 进行的,而 WebSocket 是长连接,所以同一个机器人一次连接下收发消息所用的连接是同一个,所以 Fd 也是相同的。同理,炸毛框架的内部来区分多个机器人也是通过这一 Fd 进行判定的。 +值得注意的是,由于机器人客户端和炸毛框架的连接是通过 WebSocket 进行的,而 WebSocket 是长连接,所以同一个机器人一次连接下收发消息所用的连接是同一个,所以 Fd 也是相同的。同理,炸毛框架的内部来区分多个机器人也是通过这一 Fd 进行判定的。 -=== "代码" +::: - ```php - /** - * @CQCommand("测试fd") - */ - public function testfd() { - ctx()->reply("当前机器人连接的fd是:".ctx()->getFd().",机器人QQ是:".ctx()->getRobotId()); - } - ``` +代码 -=== "效果" +```php +/** + * @CQCommand("测试fd") + */ +public function testfd() { + ctx()->reply("当前机器人连接的fd是:".ctx()->getFd().",机器人QQ是:".ctx()->getRobotId()); +} +``` - - ^ 假设我们和连接55555的机器人的私聊 - ) 测试fd - ( 当前机器人连接的fd是:1,机器人QQ是:55555 - ^ 假设切到了另一个机器人(66666)的私聊 - ) 测试fd - ( 当前机器人连接的fd是:2,机器人QQ是:66666 - +效果 + + ## getData() - 获取事件完整数据 @@ -82,11 +84,11 @@ public function onMessage() { } ``` - -^ 假设我是QQ为123456的用户,私聊发消息 -) 哈咯!! -( 消息类型是:private - + ## getRequest() - HTTP 请求对象 @@ -132,10 +134,10 @@ public function ping() { ctx()->getRobot()->sendPrivateMsg(123456, "发送私聊消息"); ``` - -^ 正在和机器人聊天 -( 发送私聊消息 - + ## getMessage() - 获取消息 @@ -143,24 +145,26 @@ ctx()->getRobot()->sendPrivateMsg(123456, "发送私聊消息"); 可以使用的事件:`@CQCommand()`,`@CQMessage`,`@CQBefore("message")`,`@CQAfter("message")` -=== "代码" - ```php - /** - * @CQMessage(group_id=33333) - */ - public function groupRepeat() { - ctx()->reply(ctx()->getMessage()); - } - ``` +代码 -=== "效果" - - ^ 现在在群33333内,机器人已经成了复读机 - ) 来世还做复读机!!! - ( 来世还做复读机!!! - ) 你不许复读! - ( 你不许复读! - +```php +/** + * @CQMessage(group_id=33333) + */ +public function groupRepeat() { + ctx()->reply(ctx()->getMessage()); +} +``` + +效果 + + ## getUserId() - 获取用户 QQ 号 @@ -275,16 +279,16 @@ function yourName(){ } ``` - -) 自我介绍 -( 你叫啥名字呀? -) jerry -( 好的,可爱的机器人记住你叫 jerry 啦!以后多聊天哦! -) 自我介绍 -( 你叫啥名字呀? -^ 10分钟没理机器人 -( 你都10分钟不理我了,嘤嘤嘤 - + ## getArgs() - 自动获取参数 @@ -316,12 +320,12 @@ public function argTest1() { } ``` - -) test -( 请输入你要传入的参数内容 -) test2 -( 参数内容:test2 - + `getArgs()` 也有三层封装,在使用过程中避免麻烦的话,推荐使用下面这几种 `get*Arg()` 方式。 @@ -341,14 +345,14 @@ public function argTest1() { } ``` - -) test abc def argtest -( 参数内容:abc def argtest -) test -( 请输入你要传入的参数内容 -) abc def -( 参数内容:abc def - + ## getNextArg() @@ -364,14 +368,14 @@ public function argTest1() { } ``` - -) test abc def argtest -( 参数内容:abc -) test -( 请输入你要传入的参数内容 -) abc -( 参数内容:abc - + ## getNumArg() @@ -389,14 +393,14 @@ public function argTest1() { } ``` - -) test abc 334 argtest -( 数字参数内容:334 -) test abc -( 请输入你要传入的数字内容 -) 998 -( 参数内容:998 - + ## copy() @@ -418,8 +422,7 @@ public function argTest1() { } ``` - -) test abc 334 argtest -( 参数内容:abc, 334, argtest - - + diff --git a/docs/component/common/coroutine-pool.md b/docs/component/common/coroutine-pool.md index 0fbd256c..0ddf7065 100644 --- a/docs/component/common/coroutine-pool.md +++ b/docs/component/common/coroutine-pool.md @@ -60,4 +60,3 @@ for($i = 0; $i < 1000; ++$i) { `$name` 为字符串,是你要用的协程池的名称。 `$size` 为大小,最大不可超过 Swoole 配置文件中指定的最大协程数量。 - diff --git a/docs/component/common/event-tracer.md b/docs/component/common/event-tracer.md index 6e8aae1e..2891ca43 100644 --- a/docs/component/common/event-tracer.md +++ b/docs/component/common/event-tracer.md @@ -101,4 +101,4 @@ public function randNum() { ## EventDispatcher::enableEventTrace() - 启用事件跟踪器 -还没写完,不着急。 \ No newline at end of file +还没写完,不着急。 diff --git a/docs/component/common/global-functions.md b/docs/component/common/global-functions.md index 2566314f..5c1c1f0d 100644 --- a/docs/component/common/global-functions.md +++ b/docs/component/common/global-functions.md @@ -8,35 +8,35 @@ 根据加载的用户编写的代码类名来获取类所在的文件路径。 -=== "src/Module/Example/Hello.php" +**src/Module/Example/Hello.php** - ```php - -vvv # nc 127.0.0.1 20002 -vvv ``` 输入 help 即可查看内置的常用指令: -![img.png](https://static.zhamao.me/images/docs/7b74aa2b487c86482097ec7692c66e08.png -) \ No newline at end of file + +![img.png](https://static.zhamao.me/images/docs/7b74aa2b487c86482097ec7692c66e08.png) diff --git a/docs/component/common/singleton-trait.md b/docs/component/common/singleton-trait.md index 9f07ce22..27dc2926 100644 --- a/docs/component/common/singleton-trait.md +++ b/docs/component/common/singleton-trait.md @@ -1,4 +1,4 @@ -# 单例类(SingletonTrait) +# 单例类 - SingletonTrait 单例类,顾名思义,就是让用户声明的类拥有单例的特性,而这一组件引入的方式也最直接。它是一个 PHP 的 `trait`。 @@ -40,4 +40,4 @@ Foo::getInstance()->test = 5; var_dump(Foo::getInstance()->test); ``` -只需要在类中使用:`use \ZM\Utils\SingletonTrait;` 一句话即可。 \ No newline at end of file +只需要在类中使用:`use \ZM\Utils\SingletonTrait;` 一句话即可。 diff --git a/docs/component/common/task-worker.md b/docs/component/common/task-worker.md index c43a73a0..b5b66a70 100644 --- a/docs/component/common/task-worker.md +++ b/docs/component/common/task-worker.md @@ -23,4 +23,3 @@ ```php TaskManager::runTask("heavy_task", 100, "param1", "param2"); ``` - diff --git a/docs/component/common/zmutil.md b/docs/component/common/zmutil.md index 68f0d795..8c55906f 100644 --- a/docs/component/common/zmutil.md +++ b/docs/component/common/zmutil.md @@ -64,4 +64,3 @@ array:31 [ ``` > 为什么不能重载所有文件?因为框架是多进程模型,而重载相当于只重新启动了一次 Worker 进程,Manager 和 Master 进程未重启,所以被 Manager、Master 进程已经加载的 PHP 文件无法使用 reload 命令重新加载。详见 [进阶 - 进程间隔离](/advanced/multi-process/#_5)。 - diff --git a/docs/component/http/route-manager.md b/docs/component/http/route-manager.md index 698e125d..b7ff851c 100644 --- a/docs/component/http/route-manager.md +++ b/docs/component/http/route-manager.md @@ -6,9 +6,11 @@ HTTP 路由管理器用作管理炸毛框架内 `@RequestMapping` 和静态目 > 2.3.0 版本起可用。 -!!! warning "注意" +::: warning 注意 - 因为炸毛框架的路由实现是不基于跨进程的共享内存的,所以每次使用这里面的工具函数都需要单独在所有 Worker 进程中执行一次,最好的办法就是在启动框架时执行(`@OnStart(-1)` 即可,代表此注解事件将在每个工作进程中都被执行一次)。 +因为炸毛框架的路由实现是不基于跨进程的共享内存的,所以每次使用这里面的工具函数都需要单独在所有 Worker 进程中执行一次,最好的办法就是在启动框架时执行(`@OnStart(-1)` 即可,代表此注解事件将在每个工作进程中都被执行一次)。 + +::: ## 方法 @@ -51,4 +53,4 @@ public function onStart() { 定义:`\Symfony\Component\Routing\RouteCollection | null` -炸毛框架使用了 Symfony 框架的 route 组件,有关详情请查阅 [文档](https://symfony.com/doc/current/routing.html)。 \ No newline at end of file +炸毛框架使用了 Symfony 框架的 route 组件,有关详情请查阅 [文档](https://symfony.com/doc/current/routing.html)。 diff --git a/docs/component/http/zmrequest.md b/docs/component/http/zmrequest.md index 46008281..9c33a758 100644 --- a/docs/component/http/zmrequest.md +++ b/docs/component/http/zmrequest.md @@ -4,10 +4,11 @@ 命名空间:`use ZM\Requests\ZMRequest;` -!!! warning "注意" +::: warning 注意 - 在使用 Swoole 4.6.0 以下(不包含)的版本时,最好使用 Swoole 官方推荐的 Saber 或者 ZMRequest 这个轻量的 HTTP 请求客户端,不要使用 curl_exec,因为在老版本的 Swoole 上对 curl 的协程 Hook 支持不是很完善。 +在使用 Swoole 4.6.0 以下(不包含)的版本时,最好使用 Swoole 官方推荐的 Saber 或者 ZMRequest 这个轻量的 HTTP 请求客户端,不要使用 curl_exec,因为在老版本的 Swoole 上对 curl 的协程 Hook 支持不是很完善。 +::: ## ZMRequest::get() @@ -262,11 +263,12 @@ $a->onClose(function($client){ 返回值:`true|false`,当为 `true` 时代表握手成功,此时可以在回调里愉快地收发消息了。如果为 `false` 表明握手失败。 -!!! warning "注意" +::: warning 注意 - 这里由于是协程转异步,所以不能确定 `upgrade()` 和 `onMessage()` 哪个先会被触发(一般情况下如果服务器不是立刻响应回包信息,总是会先返回 `upgrade()` 的结果。 +这里由于是协程转异步,所以不能确定 `upgrade()` 和 `onMessage()` 哪个先会被触发(一般情况下如果服务器不是立刻响应回包信息,总是会先返回 `upgrade()` 的结果。 + +::: ## 设置参数 见:[Swoole - HTTP 客户端](http://wiki.swoole.com/#/coroutine_client/http_client?id=set) - diff --git a/docs/component/index.md b/docs/component/index.md deleted file mode 100644 index 033320eb..00000000 --- a/docs/component/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# 框架组件 - -这里列举了框架内的你可能会用到的常用组件。 \ No newline at end of file diff --git a/docs/component/module/module-pack.md b/docs/component/module/module-pack.md index 3b08b6c2..a75ce6ca 100644 --- a/docs/component/module/module-pack.md +++ b/docs/component/module/module-pack.md @@ -36,14 +36,16 @@ src/ - 含义:模块的描述。 -??? note "点我查看编写实例:" +::: tip 编写实例 - ```json - { - "name": "my-first-module", - "description": "这个是一个示例模块打包教程" - } - ``` +```json +{ + "name": "my-first-module", + "description": "这个是一个示例模块打包教程" +} +``` + +::: #### - version @@ -52,14 +54,17 @@ src/ 版本处理方式和 Composer 基本一致,建议使用三段式,也就是 `大版本.小版本.补丁版本`。关于三段式版本的描述和规范,见 [到底三段式版本号是什么?](https://www.chrisyue.com/what-the-hell-are-semver-and-the-difference-between-composer-version-control-sign-tilde-and-caret.html)。 -??? note "点我查看编写实例:" - ```json - { - "name": "my-first-module", - "description": "这个是一个示例模块打包教程", - "version": "1.0.0" - } - ``` +::: tip 编写实例 + +```json +{ + "name": "my-first-module", + "description": "这个是一个示例模块打包教程", + "version": "1.0.0" +} +``` + +::: #### - depends @@ -68,17 +73,20 @@ src/ 此处用作模块的依赖检测,假设模块 `foo` 依赖模块 `bar` 的 1.x 版本但是不兼容 `bar` 的 2.x 版本,可以像 Composer 的 `require` 一样编写版本依赖:`^1.0`。也可以使用 `~`、`>=`、`*` 这些与 Composer 包管理相同逻辑的版本依赖关系,详见 [Composer - 包版本](https://docs.phpcomposer.com/01-basic-usage.html#Package-Versions)。 -??? note "点我查看编写实例:" - ```json - { - "name": "foo", - "description": "这个是一个示例模块打包教程", - "depends": { - "bar": "^1.0", - "bsr": "*" - } +::: tip 编写实例 + +```json +{ + "name": "foo", + "description": "这个是一个示例模块打包教程", + "depends": { + "bar": "^1.0", + "bsr": "*" } - ``` +} +``` + +::: #### - light-cache-store @@ -89,16 +97,19 @@ src/ 我们假设在项目模块中使用到了 `group-status` 这一个 LightCache,那么只需要写 `light-cache-store` 配置项,在模块打包时就会将持久化的数据也打包到 phar 模块包内。 -??? note "点我查看编写实例:" - ```json - { - "name": "foo", - "description": "这个是一个示例模块打包教程", - "light-cache-store": [ - "group-status" - ] - } - ``` +::: tip 编写实例 + +```json +{ + "name": "foo", + "description": "这个是一个示例模块打包教程", + "light-cache-store": [ + "group-status" + ] +} +``` + +::: #### - global-config-override @@ -109,14 +120,17 @@ src/ 如果是 false,那么和不指定此参数效果是一样的,无需用户修改 global.php。 -??? note "点我查看编写实例:" - ```json - { - "name": "foo", - "description": "这个是一个示例模块打包教程", - "global-config-override": "请将 static_file_server 的 status 改为 true" - } - ``` +::: tip 编写实例 + +```json +{ + "name": "foo", + "description": "这个是一个示例模块打包教程", + "global-config-override": "请将 static_file_server 的 status 改为 true" +} +``` + +::: #### - allow-hotload @@ -125,17 +139,23 @@ src/ 当此项为 true 时,可以将模块包直接放入 `zm_data/modules` 文件夹下,然后将 `global.php` 中的 `module_loader` 项中的 `enable_hotload` 改为 true,启动框架即可加载。 -??? note "点我查看编写实例:" - ```json - { - "name": "foo", - "description": "这个是一个示例模块打包教程", - "allow-hotload": true - } - ``` +::: tip 编写实例 -!!! warning "注意" - 如果使用允许热加载,那么模块包中的配置最好不要有 `global-config-override` 和 `light-cache-store`,以此来达到最正确的效果,一般热加载更适合 Library(库)类型的模块。 +```json +{ + "name": "foo", + "description": "这个是一个示例模块打包教程", + "allow-hotload": true +} +``` + +::: + +::: warning 注意 + +如果使用允许热加载,那么模块包中的配置最好不要有 `global-config-override` 和 `light-cache-store`,以此来达到最正确的效果,一般热加载更适合 Library(库)类型的模块。 + +::: #### - zm-data-store @@ -146,17 +166,20 @@ src/ 我们假设要打包一个 `{zm_data 目录}/config/` 目录及其目录下的文件,和一个 `main.png` 文件,下方是实例。 -??? note "点我查看编写实例:" - ```json - { - "name": "foo", - "description": "这个是一个示例模块打包教程", - "zm-data-store": [ - "config/", - "main.png" - ] - } - ``` +::: tip 编写实例 + +```json +{ + "name": "foo", + "description": "这个是一个示例模块打包教程", + "zm-data-store": [ + "config/", + "main.png" + ] +} +``` + +::: 在打包时框架会自动添加这些文件到 phar 插件包内,到解包时,会自动将这些文件释放到对应框架的 `zm_data` 目录下。 @@ -221,7 +244,6 @@ $ ./zhamao module:pack foo - crash/swoole_error.log - 必要的框架热加载以及解包需要的配置信息 - ## 打包命令 ```bash @@ -240,4 +262,3 @@ $ ./zhamao module:pack foo ``` 通过此命令可以查看模块相关的信息,如未打包但已配置的模块信息等。 - diff --git a/docs/component/module/module-unpack.md b/docs/component/module/module-unpack.md index ef523539..8cd35301 100644 --- a/docs/component/module/module-unpack.md +++ b/docs/component/module/module-unpack.md @@ -81,4 +81,4 @@ zm_data/modules/foo.phar 请输入修改模式,y(使用vim修改)/e(自行使用其他编辑器修改后确认)/N(默认暂不修改):[y/e/N] ``` -一般这种情况,根据第二条提示(第二条提示为打包时填入的 `global-config-override`)。如果输入 y,则会自动执行命令 `vim config/global.php`,如果输入的是 e,则会等待你手动修改完成文件,最后按回车完成修改。默认情况直接回车的话,会跳过此步骤,如果模块要求了修改但跳过修改,安装后可能会有功能缺失等问题。 \ No newline at end of file +一般这种情况,根据第二条提示(第二条提示为打包时填入的 `global-config-override`)。如果输入 y,则会自动执行命令 `vim config/global.php`,如果输入的是 e,则会等待你手动修改完成文件,最后按回车完成修改。默认情况直接回车的话,会跳过此步骤,如果模块要求了修改但跳过修改,安装后可能会有功能缺失等问题。 diff --git a/docs/component/store/atomics.md b/docs/component/store/atomics.md index 0b0fbe39..458db42f 100644 --- a/docs/component/store/atomics.md +++ b/docs/component/store/atomics.md @@ -16,10 +16,11 @@ $config['init_atomics'] = [ 这时我们就成功初始化两个原子计数器,名字分别为 `foo` 和 `bar`。 -!!! warning "注意" +::: warning 注意 - 初始化的值必须是不小于 0 的 int32 值! +初始化的值必须是不小于 0 的 int32 值! +::: ## 使用 @@ -59,7 +60,8 @@ class Hello { 设置计数的数字:`ZMAtomic::get("bar")->set(77);` -!!! note "提示" +::: tip 提示 - 还有一些不常用的方法,可以看 Swoole 官方的文档,这里就不一一列举了。 +还有一些不常用的方法,可以看 Swoole 官方的文档,这里就不一一列举了。 +::: diff --git a/docs/component/store/data-provider.md b/docs/component/store/data-provider.md index 5ffd83d9..602226c1 100644 --- a/docs/component/store/data-provider.md +++ b/docs/component/store/data-provider.md @@ -101,7 +101,8 @@ $r = file_get_contents(working_dir() . "/composer.json"); file_put_contents("/tmp/test.txt", "hello world"); ``` -!!! warning "注意" +::: warning 注意 - 在默认的情况里,框架的根目录均为可写可读的,在读写文件时务必要注意目录的位置和权限。使用 `working_dir()` 获取目录后面需要加 `/` 再追加自己的文件名或子目录名。 +在默认的情况里,框架的根目录均为可写可读的,在读写文件时务必要注意目录的位置和权限。使用 `working_dir()` 获取目录后面需要加 `/` 再追加自己的文件名或子目录名。 +::: diff --git a/docs/component/store/light-cache.md b/docs/component/store/light-cache.md index a5e9745c..a06d92e0 100644 --- a/docs/component/store/light-cache.md +++ b/docs/component/store/light-cache.md @@ -81,14 +81,14 @@ public function storeAfterRemove() { } ``` - -) store -( OK! -) storeAfterRemove -( 内容存在! -^ 等待 30 秒 -( 内容不存在! - + ### LightCache::update() @@ -218,14 +218,14 @@ public function getStore() { } ``` - -^ 我在 2021-01-05 15:21:00 发送这条消息 -) getStore -( 2021-01-05 15:20:00 -^ 这时我用 Ctrl+C 停止框架,过一会儿再启动 -) getStore -( 存储时间:2021-01-05 15:20:00 - + ### 数据加锁 @@ -250,10 +250,11 @@ public function test() { 在运行完测试后,通过 `LightCache::get("web_count")`,获取到的数你会发现不是 200000。怎么回事呢?请自行翻阅多进程开发相关的书籍哦!(或者简单理解为,有一些情况下,进程 1 执行到了 `if-else` 语句,另一个进程也执行到了这里,两次在代码层面加的数是相同的,则虽然请求了两次,但是后执行 set 的那个进程又覆盖了前一个进程执行的值,导致最终结果加了 1 而不是 2) -!!! note "提示" +::: tip 提示 - 同样的场景,使用 ZMAtomic 就不需要使用锁了。Atomic 是一句话:`add(1)` 立即加值的。而 LightCache 需要加锁的情况一般都是 `get->改值->set` 这样的代码。 +同样的场景,使用 ZMAtomic 就不需要使用锁了。Atomic 是一句话:`add(1)` 立即加值的。而 LightCache 需要加锁的情况一般都是 `get->改值->set` 这样的代码。 +::: 解决这一问题,就需要用到锁。这种情况下,我们首先考虑的是自旋锁,框架也因此内置了一个方便使用的自旋锁组件。详见下一章:自旋锁。 @@ -271,13 +272,13 @@ public function test() { ### WorkerCache 跨进程大缓存 -WorkerCache 和 LightCache 几乎完全不同,WorkerCache 存储的方式说白了就是 PHP 的静态变量,不过框架支持使用封装好的进程间通信进行跨进程读取。但由于需要设置一个存储变量的进程,所以配置文件必须先指定要将数据存到哪个 Worker/TaskWorker 进程中。关于框架内多进程的说明,请见 [进阶 - 多进程 Hack](/advanced/multi-process/)。 +WorkerCache 和 LightCache 几乎完全不同,WorkerCache 存储的方式说白了就是 PHP 的静态变量,不过框架支持使用封装好的进程间通信进行跨进程读取。但由于需要设置一个存储变量的进程,所以配置文件必须先指定要将数据存到哪个 Worker/TaskWorker 进程中。关于框架内多进程的说明,请见 [进阶 - 多进程 Hack](/advanced/multi-process)。 定义:`ZM\Store\WorkerCache`。 #### 配置 -见 [基本配置](/guide/basic-config/)。 +见 [基本配置](/guide/basic-config)。 #### WorkerCache::get() @@ -342,12 +343,11 @@ class Hello { } ``` - -) set_store hello world -( 成功! -) get_store hello -( world -) get_store foo -( 内容不存在! - - + diff --git a/docs/component/store/mysql-db.md b/docs/component/store/mysql-db.md index f362cda6..70d3c604 100644 --- a/docs/component/store/mysql-db.md +++ b/docs/component/store/mysql-db.md @@ -1,8 +1,10 @@ # MySQL 数据库(旧版组件) -!!! warning "注意" +::: warning 注意 - 此 MySQL 组件为旧版 MySQL 查询器组件,为了统一和提升对未来独立组件的兼容性,现转变为使用 `doctrine/dbal` 和 `doctrine/orm` 库来实现查询器,请转到 [MySQL 查询器]()。 +此 MySQL 组件为旧版 MySQL 查询器组件,为了统一和提升对未来独立组件的兼容性,现转变为使用 `doctrine/dbal` 和 `doctrine/orm` 库来实现查询器,请转到 [MySQL 查询器]()。 + +::: ## 配置 @@ -78,8 +80,6 @@ DB::table("admin")->where("name", "fake_admin")->count(); //SELECT count(*) FROM admin WHERE name = 'fake_admin' ``` - - ## 直接执行 SQL > 在查询器外执行的 SQL 语句都不会被缓存,都是一定会请求数据库的。 @@ -98,4 +98,4 @@ $r = DB::rawQuery("SELECT * FROM admin WHERE name = ?", ["fake_admin"]); echo $r[0]["password"]; ``` -> 参数查询已经从根本上杜绝了 SQL 注入的问题。 \ No newline at end of file +> 参数查询已经从根本上杜绝了 SQL 注入的问题。 diff --git a/docs/component/store/mysql-statement.md b/docs/component/store/mysql-statement.md new file mode 100644 index 00000000..62d84d54 --- /dev/null +++ b/docs/component/store/mysql-statement.md @@ -0,0 +1,3 @@ +# MySQLStatement + +你好啊,这里是 Statement。TODO diff --git a/docs/component/store/mysql/common-query.md b/docs/component/store/mysql.md similarity index 72% rename from docs/component/store/mysql/common-query.md rename to docs/component/store/mysql.md index c909b2eb..258371f6 100644 --- a/docs/component/store/mysql/common-query.md +++ b/docs/component/store/mysql.md @@ -1,4 +1,27 @@ -# 执行 SQL 语句 +# MySQL 数据库 + +## 简介 + +炸毛框架的数据库组件对接了 MySQL 连接池,在使用过程中无需配置即可实现 MySQL 查询,同时拥有高并发。 + +目前 2.5 版本后炸毛框架底层采用了 `doctrine/dbal` 组件,可以方便地构建 SQL 语句。 + +本章大体查询内容均以下表 `users` 为基础: + +| id | username | gender | update_time | +| -- | -------- | ------ | ----------- | +| 1 | jack | man | 2021-10-12 | +| 2 | rose | woman | 2021-10-11 | + +## 配置 + +炸毛框架的数据库组件支持原生 SQL、查询构造器,去掉了复杂的对象模型关联,同时默认为数据库连接池,使开发变得简单。 + +数据库的配置位于 `config/global.php` 文件的 `mysql_config` 段,见 [全局配置](../../../../guide/basic-config#mysql_config)。 + +如果 `mysql_config.host` 字段为空,则不创建数据库连接池,填写后将创建,且默认保持长连接。 + +## 执行 SQL 语句 在一开始,无论你做什么数据库操作,均需要获取一个 `\ZM\MySQL\MySQLWrapper` 作为你的操作对象。 @@ -7,11 +30,13 @@ $wrapper = \ZM\MySQL\MySQLManager::getWrapper(); ``` -!!! tip "提示" +::: tip 提示 - 这部分内容部分直接取自 [DBAL - Data Retrieval And Manipulation](https://www.doctrine-project.org/projects/doctrine-dbal/en/2.13/reference/data-retrieval-and-manipulation.html) 原文并直接翻译,如有实际不同请提交 Issue 反馈。 +这部分内容部分直接取自 [DBAL - Data Retrieval And Manipulation](https://www.doctrine-project.org/projects/doctrine-dbal/en/2.13/reference/data-retrieval-and-manipulation.html) 原文并直接翻译,如有实际不同请提交 Issue 反馈。 -## 执行预处理 SQL 语句 +::: + +### 执行预处理 SQL 语句 预处理查询很巧妙地解决了 SQL 注入问题,并且可以方便地绑定参数进行查询。 @@ -27,7 +52,7 @@ $stmt->bindValue(2, "jack"); $resultSet = $stmt->executeQuery(); ``` -其中 `$resultSet` 与 `Statement` 方法相似,此处的对象可能是 [数据库语句对象](../mysql-statement) 或 数据库结果对象(结果对象与语句对象的 `fetchXXX()` 部分一致)。 +其中 `$resultSet` 与 `Statement` 方法相似,此处的对象可能是 [数据库语句对象](./mysql-statement) 或 数据库结果对象(结果对象与语句对象的 `fetchXXX()` 部分一致)。 这里也可以使用命名标签,使用标签可以给相同参数处使用同一个标签: @@ -38,7 +63,7 @@ $stmt->bindValue("name", "jack"); $resultSet = $stmt->executeQuery(); ``` -## 执行常规语句 +### 执行常规语句 执行常规语句为 `statement` 方式执行,此方法执行后只返回影响的行数,而不返回结果,适用于 `UPDATE` 等语句。 @@ -48,7 +73,7 @@ $count = $wrapper->executeStatement('UPDATE users SET username = ? WHERE id = ?' echo $count; // 1 ``` -## 执行查询语句 +### 执行查询语句 为给定的 SQL 创建一个准备好的语句并将参数传递给 executeQuery 方法,然后返回结果集。此方法为上述的「预处理查询语句」的简化版,可直接在第二个参数使用 array 插入绑定参数执行。 @@ -68,7 +93,7 @@ array( */ ``` -### fetchAllAssociative() +#### fetchAllAssociative() 执行查询并将所有结果返回一个数组中。 @@ -79,7 +104,7 @@ $resultSet = $wrapper->fetchAllAssociative('SELECT * FROM user WHERE username = // 结果同 executeQuery()->fetchAllAssociative() 中 $user 的值。 ``` -### fetchAllKeyValue() +#### fetchAllKeyValue() 执行查询并将前两列分别作为键和值提取到关联数组中。 @@ -93,7 +118,7 @@ array( */ ``` -### fetchAllAssociativeIndexed() +#### fetchAllAssociativeIndexed() 执行查询并将数据作为关联数组获取,其中键代表第一列,值是其余列及其值的关联数组。 @@ -111,7 +136,7 @@ array( */ ``` -### fetchNumeric() +#### fetchNumeric() 查询并返回第一行数据,形式以数字索引方式返回每一列。 @@ -127,7 +152,7 @@ array( */ ``` -### fetchOne() +#### fetchOne() 仅返回查询结果的第一行第一列的值。 @@ -136,7 +161,7 @@ $username = $wrapper->fetchOne('SELECT username FROM users WHERE id = ?', array( echo $username; // jack ``` -### fetchAssociative() +#### fetchAssociative() 返回结果内第一行的关联数组形式的数据。 @@ -153,7 +178,7 @@ array( */ ``` -### delete() +#### delete() 删除查询操作,第一个参数为表名,第二个参数为 `['列名' => '列值']`。 @@ -163,7 +188,7 @@ $wrapper->delete('users', array('username' => 'jack')); // 等同于执行DELETE FROM user WHERE username = ? ,参数列表为('jack') ``` -### insert() +#### insert() 插入数据库一行,第一个参数为表名,第二个参数为对应数据。 @@ -172,7 +197,7 @@ $wrapper->insert('users', array('id' => 0, 'username' => 'jwage', 'gender' => 'w // INSERT INTO user (id, username, gender, update_time) VALUES (?,?,?,?) (0,jwage,woman,2021-10-17) ``` -### update() +#### update() 更新数据库,使用给定数据更新匹配键值标识符的所有行。 @@ -181,4 +206,3 @@ $wrapper->insert('users', array('id' => 0, 'username' => 'jwage', 'gender' => 'w $wrapper->update('user', array('username' => 'jwage'), array('id' => 1)); // UPDATE user (username) VALUES (?) WHERE id = ? (jwage, 1) ``` - diff --git a/docs/component/store/mysql/config.md b/docs/component/store/mysql/config.md deleted file mode 100644 index 6ef8d150..00000000 --- a/docs/component/store/mysql/config.md +++ /dev/null @@ -1,7 +0,0 @@ -# 配置 - -炸毛框架的数据库组件支持原生 SQL、查询构造器,去掉了复杂的对象模型关联,同时默认为数据库连接池,使开发变得简单。 - -数据库的配置位于 `config/global.php` 文件的 `mysql_config` 段,见 [全局配置](../../../../guide/basic-config#mysql_config)。 - -如果 `mysql_config.host` 字段为空,则不创建数据库连接池,填写后将创建,且默认保持长连接。 \ No newline at end of file diff --git a/docs/component/store/mysql/mysql-statement.md b/docs/component/store/mysql/mysql-statement.md deleted file mode 100644 index 1d6c775d..00000000 --- a/docs/component/store/mysql/mysql-statement.md +++ /dev/null @@ -1 +0,0 @@ -你好啊,这里是 Statement。 \ No newline at end of file diff --git a/docs/component/store/mysql/mysql.md b/docs/component/store/mysql/mysql.md deleted file mode 100644 index a91ee506..00000000 --- a/docs/component/store/mysql/mysql.md +++ /dev/null @@ -1,14 +0,0 @@ -# MySQL 数据库简介 - -炸毛框架的数据库组件对接了 MySQL 连接池,在使用过程中无需配置即可实现 MySQL 查询,同时拥有高并发。 - -目前 2.5 版本后炸毛框架底层采用了 `doctrine/dbal` 组件,可以方便地构建 SQL 语句。 - -本章大体查询内容均以下表 `users` 为基础: - -| id | username | gender | update_time | -| -- | -------- | ------ | ----------- | -| 1 | jack | man | 2021-10-12 | -| 2 | rose | woman | 2021-10-11 | - -# \ No newline at end of file diff --git a/docs/component/store/redis.md b/docs/component/store/redis.md index e76f6af0..3c95cd98 100644 --- a/docs/component/store/redis.md +++ b/docs/component/store/redis.md @@ -59,4 +59,4 @@ ZMRedis::call(function($redis) { 选一个喜欢的就好。硬要是说区别的话,对象模式是在 PHP 自动回收这个 `ZMRedis` 对象时会归还连接,也可以通过手动 `unset($obj)` 进行回收,否则就会执行到函数结尾自动回收。切记不可将 `$obj` 对象持久化存到静态或全局变量等。 -回调模式看似是回调,但是是同步执行的,不会发生顺序错乱。也就是说到了 `ZMRedis::call()` 方法里面的时候,后面的代码不会提前执行,是顺序执行的。回调的作用仅仅是用作自动回收连接对象。 \ No newline at end of file +回调模式看似是回调,但是是同步执行的,不会发生顺序错乱。也就是说到了 `ZMRedis::call()` 方法里面的时候,后面的代码不会提前执行,是顺序执行的。回调的作用仅仅是用作自动回收连接对象。 diff --git a/docs/component/store/spin-lock.md b/docs/component/store/spin-lock.md index e279f32a..4a5998eb 100644 --- a/docs/component/store/spin-lock.md +++ b/docs/component/store/spin-lock.md @@ -2,9 +2,11 @@ 前面讲到 LightCache 轻量缓存在特定的情况下为了保证数据不被多进程的因素导致丢失或覆盖,在高并发情况下修改数据需要加锁,所以炸毛框架内置了 SpinLock 自旋锁。 -!!! tip "提示" +::: tip 提示 - 框架单进程运行的模式下不需要任何自旋锁。 +框架单进程运行的模式下不需要任何自旋锁。 + +::: ## 配置 @@ -61,9 +63,11 @@ public function test() { 原理剖析:在 LightCache 获取前,先对此内容上锁,这时如果其他进程有同时也在执行这个代码的时候,就会在 `SpinLock::lock()` 这行代码处原地等待,防止继续执行。等前面的那个进程执行到 `SpinLock::unlock()` 释放锁时,其他进程才可继续执行,从而避免了多个进程并行执行这段代码导致的数据错乱。 -!!! error "警告" +::: danger 警告 - 使用锁时务必谨慎,如果不按照下面的规则使用自旋锁可能导致 CPU 占用率上升。 +使用锁时务必谨慎,如果不按照下面的规则使用自旋锁可能导致 CPU 占用率上升。 + +::: 自旋锁使用约定: diff --git a/docs/event/custom-annotations.md b/docs/event/custom-annotations.md index cda23651..c054602f 100644 --- a/docs/event/custom-annotations.md +++ b/docs/event/custom-annotations.md @@ -1,3 +1,3 @@ # 自定义注解 -TODO:师傅,莫催,快肝完了! \ No newline at end of file +TODO:师傅,莫催,快肝完了! diff --git a/docs/event/event-dispatcher.md b/docs/event/event-dispatcher.md index 08c547e7..1857ba4e 100644 --- a/docs/event/event-dispatcher.md +++ b/docs/event/event-dispatcher.md @@ -116,4 +116,4 @@ class Test { 我们假设 CustomEvent 是我们的自定义注解。还没写完,这部分太复杂了,而且举例子也不好举例,这块应该也不用着急更新。 -TODO:待完成 \ No newline at end of file +TODO:待完成 diff --git a/docs/event/framework-annotations.md b/docs/event/framework-annotations.md index bdcf615a..a765834a 100644 --- a/docs/event/framework-annotations.md +++ b/docs/event/framework-annotations.md @@ -328,7 +328,7 @@ class Hello { ## 示例3(接收 WS 客户端发来的数据) -见 [接入 WebSocket 客户端](/advanced/connect-ws-client/)。 +见 [接入 WebSocket 客户端](/advanced/connect-ws-client)。 ## 示例4(使用 OnStart 给所有 Worker 进程写入缓存提速) @@ -359,8 +359,7 @@ class Hello { }, "key": "C", "answer_type": 0 - }, - ..... + } } ``` @@ -411,10 +410,10 @@ class Hello { 聊天效果: - -) 找题 1 -( 题目名称:法律与其他社会规范的区别在于( )。\nA. 是调整人们行为的规范\nB. 有约束力\nC. 由国家强制力保证执行\nD. 规定制裁措施\n正确答案:C - + ## 示例5(创建每分钟自动执行的爬虫) @@ -430,4 +429,5 @@ public function onCrawl() { ## 示例6(创建一个远程终端命令并调试框架) -> 开个坑,以后填。(__填坑标记__) \ No newline at end of file +> 开个坑,以后填。(__填坑标记__) +> \ No newline at end of file diff --git a/docs/event/index.md b/docs/event/index.md index be3237fe..f7b24373 100644 --- a/docs/event/index.md +++ b/docs/event/index.md @@ -65,6 +65,3 @@ class Hello { EventDispatcher::interrupt(); EventDispatcher::interrupt($data); // 也可以带返回值,自定义注解事件时有用。 ``` - - - diff --git a/docs/event/middleware.md b/docs/event/middleware.md index 41b263d8..a97fc70a 100644 --- a/docs/event/middleware.md +++ b/docs/event/middleware.md @@ -4,7 +4,7 @@ 在炸毛框架中,中间件最直白的意思就是注解事件执行前、执行后、执行过程中可进行插入代码但不破坏原有代码。 -```伪代码 +``` @中间件1 @带条件的注解1 function 我的方法() { @@ -192,4 +192,3 @@ public function testRoute() { 配置项为 1,在访问此路由执行此函数时会抛出异常,中断此次事件。 配置项为 2,在框架启动时抛出致命异常。 - diff --git a/docs/event/robot-annotations.md b/docs/event/robot-annotations.md index 53f69dee..7a5790e1 100644 --- a/docs/event/robot-annotations.md +++ b/docs/event/robot-annotations.md @@ -4,9 +4,11 @@ QQ 机器人事件是指 CQHTTP 插件发来的 Event 事件,被框架处理 为了便于开发,这里的注解类对应 CQHTTP 插件返回的 `post_type` 类型,对号入座即可。 -!!! tip "提示" +::: tip 提示 - 在使用注解绑定事件过程中,如果无 **必需** 参数,可一个参数也不写,效果就是此事件任何情况下都会调用此方法。例如:`@CQMessage()` +在使用注解绑定事件过程中,如果无 **必需** 参数,可一个参数也不写,效果就是此事件任何情况下都会调用此方法。例如:`@CQMessage()` + +::: 事件是用户需要从 OneBot 被动接收的数据,有以下几个大类: @@ -61,39 +63,39 @@ QQ 收到消息后触发的事件对应注解。 - 在用户 QQ 为 `123456` 的用户私聊给机器人发消息后机器人回复内容。 - 用户发送文字为 `hello` 时返回 `你好啊,xxx` 的消息。 -=== "代码" +代码 - ```php - getMessage(); - } - /** - * @CQMessage(message="hello") - */ - public function hello() { - return "你好啊,".ctx()->getUserId(); - } +```php +getMessage(); } - ``` + /** + * @CQMessage(message="hello") + */ + public function hello() { + return "你好啊,".ctx()->getUserId(); + } +} +``` -=== "效果" +效果 - - ) 假设我是私聊机器人 - ( 你和机器人私聊发送了这些文本:假设我是私聊机器人 - ^ 假设我现在切到群里,在群里发hello - ) hello - ( 你好啊,123456 - + ## CQCommand() @@ -125,11 +127,13 @@ QQ 收到消息后触发的事件对应注解。 | group_id | `int64` 或 `string` | 限定消息发送来源群 ID,同 `@CQMessage` | 空 | | level | `int` | 事件优先级(越大越靠前) | 20 | -!!! warning "注意" +::: warning 注意 - 在 `@CQCommand` 注解事件中,从 `match` 到 `keyword` 六个参数中,必须且只能定义一个,`alias` 目前只能和 `match` 参数同时使用; - - 框架内部对于同一条消息事件,优先处理 `@CQCommand` 注解事件,如果未匹配到任何注解事件,则才会继续执行 `@CQMessage` 注解事件。 +在 `@CQCommand` 注解事件中,从 `match` 到 `keyword` 六个参数中,必须且只能定义一个,`alias` 目前只能和 `match` 参数同时使用; + +框架内部对于同一条消息事件,优先处理 `@CQCommand` 注解事件,如果未匹配到任何注解事件,则才会继续执行 `@CQMessage` 注解事件。 + +::: - 参数 `match` 匹配模式是:遇到空格、换行就会切分,比如 `点歌 xxx yyy` 会被分割为 `[点歌,xxx,yyy]`,然后抽取第一个词做为命令去匹配,剩下的为参数。 - 参数 `pattern` 匹配模式是:\* 号位置变成参数,比如 `从*到*的随机数`,我们输入 `从1到9的随机数`,成功匹配,参数列表:`[1,9]`。 @@ -141,52 +145,54 @@ QQ 收到消息后触发的事件对应注解。 我们以参数 `match` 写一个简单的 demo: -=== "代码" - ```php - getNextArg("请输入城市名称"); - return "城市 ".$city." 的疫情状况如下:"."{这里假装是疫情接口返回的数据}"; - } - /** - * 如果选择使用 match 参数的话,可以省略 `match=` - * @CQCommand("掷硬币") - */ - public function randChoice() { - return "你看到的是:" . (mt_rand(0,1) ? "正面" : "反面"); - } - /** - * @CQCommand(pattern="*把*翻译成*") - */ - public function translate() { - ctx()->getNextArg(); // 为什么需要单独调用一次呢?看下面例子就知道啦 - $text = ctx()->getNextArg(); // 获取第二个星号匹配的内容 - $target = ctx()->getNextArg(); // 获取第三个星号匹配的内容 - // 这里 FakeTranslateAPI 是假设我们对接了一个翻译的 API,开发时请替换为自己的接口。 - return "翻译结果:" . FakeTranslateAPI::translate($text, $target); - } +```php +getNextArg("请输入城市名称"); + return "城市 ".$city." 的疫情状况如下:"."{这里假装是疫情接口返回的数据}"; } - ``` -=== "效果" + /** + * 如果选择使用 match 参数的话,可以省略 `match=` + * @CQCommand("掷硬币") + */ + public function randChoice() { + return "你看到的是:" . (mt_rand(0,1) ? "正面" : "反面"); + } + /** + * @CQCommand(pattern="*把*翻译成*") + */ + public function translate() { + ctx()->getNextArg(); // 为什么需要单独调用一次呢?看下面例子就知道啦 + $text = ctx()->getNextArg(); // 获取第二个星号匹配的内容 + $target = ctx()->getNextArg(); // 获取第三个星号匹配的内容 + // 这里 FakeTranslateAPI 是假设我们对接了一个翻译的 API,开发时请替换为自己的接口。 + return "翻译结果:" . FakeTranslateAPI::translate($text, $target); + } +} +``` - - ) 疫情 北京 - ( 城市 北京 的疫情状况如下:blablablabla - ) COVID 香港 - ( 城市 香港 的疫情状况如下:blablablabla - ) 掷硬币 - ( 你看到的是:正面 - ) 我想把我爱你翻译成英语 - ( 翻译结果:I love you! - +效果 + + ## CQNotice() @@ -292,49 +298,52 @@ TODO:先放着,有时间再更。 ### 用法 -=== "代码" +代码 - ```php - getMessage(), "谷歌") !== false) return false; - else return true; - } - /** - * @CQCommand("百科") - */ - public function wiki() { - $content = ctx()->getNextArg("请说你要查百科的内容"); - // 这里假设你对接了一个查百科的接口 - return "已搜到匹配 $content 的如下结果:".FakeAPI::searchWiki($content); - } +```php +getMessage(), "谷歌") !== false) return false; + else return true; } - ``` + /** + * @CQCommand("百科") + */ + public function wiki() { + $content = ctx()->getNextArg("请说你要查百科的内容"); + // 这里假设你对接了一个查百科的接口 + return "已搜到匹配 $content 的如下结果:".FakeAPI::searchWiki($content); + } +} +``` -=== "效果" +效果 - - ) 百科 北京 - ( 已搜到匹配 北京 的如下结果:blablabla - ) 百科 谷歌被封 - ^ 机器人没有任何回复 + -!!! warning "注意" +::: warning 注意 - 在设置了 `level` 参数后,如果设置了多个 `@CQBefore` 监听事件函数,更高 `level` 的事件函数返回了 `false`,则低 `level` 的绑定函数不会执行,所有 `@CQMessage` 绑定的事件也不会执行。 - - 你也可以使用 `@CQBefore` 做一些消息的转发和过滤。比如你想去除用户发来的文字中的 emoji、图片等 CQ 码,只保留文本。 - - 使用 `ctx()->waitMessage()` 时等待用户输入下一条消息功能和 CQBefore 配合过滤消息时需注意,见 [FAQ - CQBefore 过滤不了 waitMessage](/FAQ/wait-message-cqbefore/) +在设置了 `level` 参数后,如果设置了多个 `@CQBefore` 监听事件函数,更高 `level` 的事件函数返回了 `false`,则低 `level` 的绑定函数不会执行,所有 `@CQMessage` 绑定的事件也不会执行。 + +你也可以使用 `@CQBefore` 做一些消息的转发和过滤。比如你想去除用户发来的文字中的 emoji、图片等 CQ 码,只保留文本。 + +使用 `ctx()->waitMessage()` 时等待用户输入下一条消息功能和 CQBefore 配合过滤消息时需注意,见 [FAQ - CQBefore 过滤不了 waitMessage](/FAQ/wait-message-cqbefore/) + +::: ## CQAfter() diff --git a/docs/event/route-annotations.md b/docs/event/route-annotations.md index a01ad652..e250dc96 100644 --- a/docs/event/route-annotations.md +++ b/docs/event/route-annotations.md @@ -2,9 +2,11 @@ 炸毛框架提供了一个简易但是高效易用的 HTTP 路由注解,你可以使用路由功能来开发任何 Web 应用微服务、API 接口、中间件等。 -!!! quote "开发提示" +::: tip 开发提示 - 本章节涉及的路由和控制器概念可能和其他传统框架有一些出入,而且炸毛框架非绝对根据 PSR 标准进行开发,目的是使用上一些常见的东西尽可能地灵活和不啰嗦。 +本章节涉及的路由和控制器概念可能和其他传统框架有一些出入,而且炸毛框架非绝对根据 PSR 标准进行开发,目的是使用上一些常见的东西尽可能地灵活和不啰嗦。 + +::: ## 控制器和路由 @@ -62,50 +64,54 @@ public function testName($param) { } ``` - - ### 路由示例 -=== "代码" - ```php - getResponse()->end("This is API index page"); // 使用上下文获取响应对象 - } - /** - * @RequestMapping("/ping") - */ - public function ping(){ - return "pong"; // 直接返回字符串 - } + public function index(){ + ctx()->getResponse()->end("This is API index page"); // 使用上下文获取响应对象 } - ``` + /** + * @RequestMapping("/ping") + */ + public function ping(){ + return "pong"; // 直接返回字符串 + } +} +``` -=== "效果" +效果 - !!! example "效果描述" - 当访问浏览器的 `http://localhost:20001/api/index` 时,浏览器会返回 `This is API index page`,当访问 `/api/ping` 的 url 时,浏览器会返回 `pong`。 - - ``` - / -> 无任何路由 - /api/index -> Hello->index - /api/ping -> Hello->ping - ``` +::: tip 效果描述 -!!! tip "提示" +当访问浏览器的 `http://localhost:20001/api/index` 时,浏览器会返回 `This is API index page`,当访问 `/api/ping` 的 url 时,浏览器会返回 `pong`。 - 当 `@Controller` 为 `/` 的时候,效果和不写是一样的,`@RequestMapping` 为 `/` 或 `/index/inside` 等多级路由也是可以的。 +``` +/ -> 无任何路由 +/api/index -> Hello->index +/api/ping -> Hello->ping +``` + +::: + +::: tip 提示 + +当 `@Controller` 为 `/` 的时候,效果和不写是一样的,`@RequestMapping` 为 `/` 或 `/index/inside` 等多级路由也是可以的。 + +::: ### 绑定参数 @@ -126,46 +132,46 @@ public function index($arg) { ### 示例 -=== "获取 GET" +#### 获取 GET - ```php - /** - * @RequestMapping("/testUrl") - */ - public function testUrl() { - $get = ctx()->getRequest()->get; - if(isset($get["name"])) return "hello, ".$get["name"]; - else return "Unknown name!!"; - } - ``` +```php +/** + * @RequestMapping("/testUrl") + */ +public function testUrl() { + $get = ctx()->getRequest()->get; + if(isset($get["name"])) return "hello, ".$get["name"]; + else return "Unknown name!!"; +} +``` -=== "获取 POST(x-www-form-urlencoded)" +#### 获取 POST(x-www-form-urlencoded) - ```php - /** - * @RequestMapping("/testUrl") - */ - public function testUrl() { - $post = ctx()->getRequest()->post; - if(isset($post["name"])) return "hello, ".$post["name"]; - else return "Unknown name!!"; - } - ``` +```php +/** + * @RequestMapping("/testUrl") + */ +public function testUrl() { + $post = ctx()->getRequest()->post; + if(isset($post["name"])) return "hello, ".$post["name"]; + else return "Unknown name!!"; +} +``` -=== "获取 JSON POST" +#### 获取 JSON POST - ```php - /** - * @RequestMapping("/testUrl") - */ - public function testUrl() { - $post = ctx()->getRequest()->rawContent(); - $json = json_decode($post, true); - if ($json === null) return "Invalid json data!"; - if(isset($json["name"])) return "hello, ".$json["name"]; - else return "Unknown name!!"; - } - ``` +```php +/** + * @RequestMapping("/testUrl") + */ +public function testUrl() { + $post = ctx()->getRequest()->rawContent(); + $json = json_decode($post, true); + if ($json === null) return "Invalid json data!"; + if(isset($json["name"])) return "hello, ".$json["name"]; + else return "Unknown name!!"; +} +``` ## 设置路由请求方式 diff --git a/docs/faq/FAQ.md b/docs/faq/README.md similarity index 98% rename from docs/faq/FAQ.md rename to docs/faq/README.md index 41e592dd..e4fccb25 100644 --- a/docs/faq/FAQ.md +++ b/docs/faq/README.md @@ -4,4 +4,4 @@ 如果框架运行过程中发现带有错误码(如 `E00034` 的形式),可以到 [错误码](/guide/errcode) 查看。 -框架的常见问题见 [常见问题汇总](/faq/usual-question)。 \ No newline at end of file +框架的常见问题见 [常见问题汇总](/faq/usual-question)。 diff --git a/docs/faq/address-already-in-use.md b/docs/faq/address-already-in-use.md index 6f53d4e8..73a10c96 100644 --- a/docs/faq/address-already-in-use.md +++ b/docs/faq/address-already-in-use.md @@ -16,4 +16,4 @@ ps aux | grep vendor/bin/start | grep -v grep | awk '{print $2}' | xargs kill -9 # 然后使用下面的这条命令,假设最小的pid是23643 kill -INT 23643 # 如果使用 ps aux 看不到框架相关进程,证明关闭成功,否则需要使用第一条强行杀死 -``` \ No newline at end of file +``` diff --git a/docs/faq/display-deadlock.md b/docs/faq/display-deadlock.md index e2c6b2bd..08f258fd 100644 --- a/docs/faq/display-deadlock.md +++ b/docs/faq/display-deadlock.md @@ -19,4 +19,4 @@ 这种错误的出现原因一般是因为协程未结束而 Worker 进程提前退出导致的,这个错误也可手动造成(在任意 Worker 进程内的位置使用 `zm_yield()` 且不使用 `zm_resume()` 恢复,期间使用 reload 或 stop 重启或停止框架就会报错)。 -还有一种情况是数据库、文件读取或下载上传还没有传送结束,时间已经超时,在关闭或重启框架时不得不强行切断协程的运行。这种情况建议根据下方的打印输出栈进行插错,建议将协程运行时间长的过程缩短或调长 `swoole` 配置项下面的 `max_wait_time` 时间(秒),2.4.3 版本起此参数默认为 5 秒。 \ No newline at end of file +还有一种情况是数据库、文件读取或下载上传还没有传送结束,时间已经超时,在关闭或重启框架时不得不强行切断协程的运行。这种情况建议根据下方的打印输出栈进行插错,建议将协程运行时间长的过程缩短或调长 `swoole` 配置项下面的 `max_wait_time` 时间(秒),2.4.3 版本起此参数默认为 5 秒。 diff --git a/docs/faq/light-cache-wrong.md b/docs/faq/light-cache-wrong.md index d67f1e3b..176ce6a7 100644 --- a/docs/faq/light-cache-wrong.md +++ b/docs/faq/light-cache-wrong.md @@ -2,4 +2,4 @@ LightCache 因为是跨内存使用的,所以每次重启和关闭框架时,都只会让其中一个进程去保存。因为在 2.4.2 版本开始,持久化的逻辑发生了更改,不再支持 `expire = -2` 进行设置持久化(因为那样会很容易让开发者写错),仅支持使用 `LightCache::addPersistence($key)` 这样的方式进行设置持久化,所以在 2.4.2 版本以后,请使用此方法进行持久化设置,保证数据不丢失。 -此外,2.4.2 版本起,不再支持用户手动调用 `savePersistence()` 方法,普通用户不可手动调用此方法,否则会导致数据出错。 \ No newline at end of file +此外,2.4.2 版本起,不再支持用户手动调用 `savePersistence()` 方法,普通用户不可手动调用此方法,否则会导致数据出错。 diff --git a/docs/faq/to-v2.md b/docs/faq/to-v2.md index 5da3fc87..8b675da9 100644 --- a/docs/faq/to-v2.md +++ b/docs/faq/to-v2.md @@ -36,4 +36,4 @@ 由于 2.0 框架使用了多进程模型,所以不能使用原先适用于单进程下全局变量的方式(ZMBuf)进行存取变量,所以 ZMBuf 下的所有方法都需要更改,其中 `get, set` 等对缓存操作的模型请根据 2.0 的文档变更使用 `Redis` 或内置的多进程共享内存可用的 `LightCache` 轻量缓存。 -而获取全局配置文件,如 `global.php` 文件,也发生了变化,新框架引入了 `ZMConfig` 对象,可以快速地区分各类环境变量从而读取不同的配置文件。比如我们获取原先的 global 配置文件中的一项:`ZMBuf::globals("port")`,在 2.0 中需要使用 `ZMConfig::get("global", "port")` 方式。以此类推,`ZMBuf::config("xxx")` 也直接变为 `ZMConfig::get("xxx")` 了。 \ No newline at end of file +而获取全局配置文件,如 `global.php` 文件,也发生了变化,新框架引入了 `ZMConfig` 对象,可以快速地区分各类环境变量从而读取不同的配置文件。比如我们获取原先的 global 配置文件中的一项:`ZMBuf::globals("port")`,在 2.0 中需要使用 `ZMConfig::get("global", "port")` 方式。以此类推,`ZMBuf::config("xxx")` 也直接变为 `ZMConfig::get("xxx")` 了。 diff --git a/docs/faq/usual-question.md b/docs/faq/usual-question.md index cb2cba1f..79495641 100644 --- a/docs/faq/usual-question.md +++ b/docs/faq/usual-question.md @@ -15,9 +15,11 @@ ### v2.6.6 及以下版本教程 -!!! warning "注意" +::: warning 注意 - 下方涉及 `ps` 命令后使用 `grep` 过滤的框架进程方式,如果你的服务器同时有其他使用 PHP 启动的服务,命令行刚好有 `server` 字样,可能会导致误杀,如果有影响的话,建议将 `grep server` 换成你启动时命令行的特殊参数或手动排除! +下方涉及 `ps` 命令后使用 `grep` 过滤的框架进程方式,如果你的服务器同时有其他使用 PHP 启动的服务,命令行刚好有 `server` 字样,可能会导致误杀,如果有影响的话,建议将 `grep server` 换成你启动时命令行的特殊参数或手动排除! + +::: **一、**首先,使用 `ps`、`htop`、`netstat -nlp` 等命令确定框架的入口进程(也就是 Master 进程的 pid)。 @@ -61,6 +63,3 @@ Manager 进程下的子进程,连号部分为对应的 Worker 进程,比如 `htop` 使用方向键选择进程,选择到对应进程后可以使用 `F9` 来选择 kill 指令,比如让框架热重启,可以将光标移到 Master 进程上,使用 `SIGUSR1`: ![image-20210708004921655](https://static.zhamao.me/images/docs/image-20210708004921655.png) - - - diff --git a/docs/faq/wait-message-cqbefore.md b/docs/faq/wait-message-cqbefore.md index 688199b9..b946a0a0 100644 --- a/docs/faq/wait-message-cqbefore.md +++ b/docs/faq/wait-message-cqbefore.md @@ -20,4 +20,4 @@ public function filter2() { } ``` -如果 `level >= 200`,那么此注解事件则会过滤 `waitMessage()`,如果 `level < 200`,则不会。(`@CQBefore` 的默认 level 为 20,所以默认情况下是不会过滤 waitMessage 的) \ No newline at end of file +如果 `level >= 200`,那么此注解事件则会过滤 `waitMessage()`,如果 `level < 200`,则不会。(`@CQBefore` 的默认 level 为 20,所以默认情况下是不会过滤 waitMessage 的) diff --git a/docs/guide/README.md b/docs/guide/README.md new file mode 100644 index 00000000..a087abb9 --- /dev/null +++ b/docs/guide/README.md @@ -0,0 +1,50 @@ +# 介绍 + +::: tip 提示 + +编写文档需要较大精力,你也可以参与到本文档的建设中来,比如找错字,增加或更正内容,每页文档可直接点击右上方铅笔图标直接跳转至 GitHub 进行编辑,编辑后自动 Fork 并生成 Pull Request,以此来贡献此文档! + +::: + +炸毛框架使用 PHP 编写,采用 Swoole 扩展为基础,主要面向 API 服务,聊天机器人(OneBot 标准的机器人对接),包含 WebSocket、HTTP 等监听和请求库,用户代码采用模块化处理,使用注解可以方便地编写各类功能。 + +框架主要用途为 HTTP/WebSocket 服务器,机器人搭建框架。尤其对于聊天机器人消息处理较为方便和全面,提供了众多会话机制和内部调用机制,可以以各种方式设计你自己的模块。 + +此外,QQ 机器人方面此框架基于 OneBot 标准的反向 WebSocket 连接,比传统 HTTP 通信更快。 + +```php +/** + * @CQCommand("你好") + */ +public function hello() { + ctx()->reply("你好,我是炸毛!"); +} +/** + * @RequestMapping("/index") + */ +public function index() { + return "

hello!

"; +} +``` + + + +## 开始前 + +首先,你需要了解你需要知道哪些事情才能开始着手使用框架: + +1. Linux 命令行(会跑 Linux 程序) +2. php >=7.2 开发环境(项目会持续支持最新的 PHP 版本) +4. OneBot 机器人聊天接口标准 + +需要值得注意的是,本教程中所涉及的内容均为尽可能翻译为白话的方式进行描述,但对于框架的组件或事件等需要单独拆分说明文档的部分则需要足够详细,所以本教程提供一个快速上手的教程,并且会将最典型的安装方式写到快速教程篇。 + +## 框架特色 + +- 支持MySQL数据库(连接池),自带查询缓存提高多查询时的效率 +- Websocket 服务器、HTTP 服务器兼容运行,一个框架多个用处 +- 支持命令、自然语言处理等多种插件形式 +- 支持多个机器人账号负载均衡 +- 协程 + TaskWorker 进程重度任务处理机制,保证高效,单个请求响应时间为 0.1 ms 左右 +- 模块分离和自由组合,可根据自身需求自己建立模块内的目录结构和代码结构 +- 灵活的注释注解注册事件方式,弥补 PHP 语言缺少注解的遗憾 diff --git a/docs/guide/basic-config.md b/docs/guide/basic-config.md index cf5f5fe5..1f3f9f37 100644 --- a/docs/guide/basic-config.md +++ b/docs/guide/basic-config.md @@ -2,9 +2,11 @@ 到目前为止,炸毛框架的配置文件还没有任何变更,是默认的行为。在本章内容中,将列举出炸毛框架的配置文件的规则和使用。 -!!! error "警告" +::: danger 警告 - 因为炸毛框架的全局配置中含有数据库名称和密码以及 access_token 等敏感字段,在使用版本控制软件过程中请不要将敏感信息写入配置文件并提交至开源仓库! +因为炸毛框架的全局配置中含有数据库名称和密码以及 access_token 等敏感字段,在使用版本控制软件过程中请不要将敏感信息写入配置文件并提交至开源仓库! + +::: ## 全局配置文件 global.php diff --git a/docs/guide/errcode.md b/docs/guide/errcode.md index bcc0bb9e..7aa819b5 100644 --- a/docs/guide/errcode.md +++ b/docs/guide/errcode.md @@ -1,3 +1,5 @@ +# 错误码对照表 + | 异常码 | 含义 | 解决方案 | | ------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | E00001 | 炸毛框架未检测到 PHP 安装了 Swoole 扩展 | 根据文档安装扩展去! | @@ -75,4 +77,3 @@ | E00073 | 在类中找不到方法 | 检查调用对象是否存在对应的方法(method)或检查是否插入了对应的macro宏方法 | | E00074 | 参数非法 | 检查调用的参数是否正常(此处可能有多处问题,请看具体调用栈炸掉的地方) | | E99999 | 未知错误 | | - diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 9c76072d..e5c804b5 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -44,7 +44,6 @@ docker run -it --rm -v $(pwd):/app/ -p 20001:20001 zmbot/swoole vendor/bin/start docker run -it --rm -v $(pwd):/app/ -p 20001:20001 zmbot/swoole vendor/bin/start server ``` - 启动后你会看到和下方类似的初始化内容,表明启动成功了 ```verilog @@ -80,4 +79,4 @@ $ vendor/bin/start server 我们使用文本编辑器进行炸毛框架开发,在使用集成开发环境 **IDEA** 或 **PhpStorm** 时,推荐通过插件市场搜索并安装 **PHP Annotations** 插件以提供注解命名空间自动补全、注解属性代码提醒、注解类跳转等,非常有助于提升开发效率的功能。 ## 进阶环境部署和开发 -炸毛框架还支持更多种启动方式,如源码模式、守护进程模式,具体后续有关环境和部署的进阶教程,请查看 [进阶开发](/advanced/) 部分! \ No newline at end of file +炸毛框架还支持更多种启动方式,如源码模式、守护进程模式,具体后续有关环境和部署的进阶教程,请查看 [进阶开发](/advanced/) 部分! diff --git a/docs/guide/onebot-choose.md b/docs/guide/onebot-choose.md index e7668ea1..dc5ce811 100644 --- a/docs/guide/onebot-choose.md +++ b/docs/guide/onebot-choose.md @@ -2,7 +2,7 @@ ## 什么是 OneBot -OneBot 是一个聊天机器人应用接口标准,详情戳[这里](https://github.com/howmanybots/onebot)。 +OneBot 是一个聊天机器人应用接口标准,详情戳 [这里](https://github.com/howmanybots/onebot)。 ## OneBot 实现选择 @@ -20,4 +20,4 @@ OneBot 是一个聊天机器人应用接口标准,详情戳[这里](https://gi 因为目前炸毛框架 2.0 只支持 WebSocket 方式的 OneBot 实现,所以目前上述项目的连接方式均只可选支持反向 WebSocket 通信的。后期会兼容 HTTP 和正向 WebSocket 通信方式。 -如果你还没有自己的 QQ,或者是其他原因导致的暂时无法使用上述 OneBot 实例,可以使用炸毛项目中的 OneBot 协议聊天模拟器。但目前还处在开发中,暂不可用。 \ No newline at end of file +如果你还没有自己的 QQ,或者是其他原因导致的暂时无法使用上述 OneBot 实例,可以使用炸毛项目中的 OneBot 协议聊天模拟器。但目前还处在开发中,暂不可用。 diff --git a/docs/guide/quickstart-http.md b/docs/guide/quickstart-http.md index a334699e..db3910e9 100644 --- a/docs/guide/quickstart-http.md +++ b/docs/guide/quickstart-http.md @@ -7,4 +7,3 @@ HTTP 服务器篇主要讲解如何通过炸毛框架来实现微服务、API - [HTTP 服务器 - 存储 - Redis](/component/redis/) - [HTTP 服务器 - 存储 - MySQL](/component/mysql/) - [HTTP 客户端](/component/zmrequest/) - diff --git a/docs/guide/quickstart-robot.md b/docs/guide/quickstart-robot.md index 64b0887f..dbc9cdd3 100644 --- a/docs/guide/quickstart-robot.md +++ b/docs/guide/quickstart-robot.md @@ -1,7 +1,5 @@ # 快速上手 - 机器人篇 - - ## 简介 看到这里,你已经完成了前面的环境部署,到了最关键的第一步了! @@ -26,268 +24,107 @@ OneBot 机器人部分的选择详情见 [OneBot 实例](/guide/OneBot实例/) 2. 双击 exe 文件或者使用 `./go-cqhttp` 启动 3. 生成默认配置文件并修改默认配置 -!!! warning "注意" +::: warning 注意 - 由于 go-cqhttp 项目还处于开发期,而且配置文件格式也发生了多次变化,但大体内容没有变(比如编写此文档时发布的版本中配置文件格式变成了 `hjson` 取代了原来的 `json`。 +由于 go-cqhttp 项目还处于开发期,而且配置文件格式也发生了多次变化,但大体内容没有变(比如编写此文档时发布的版本中配置文件格式变成了 `hjson` 取代了原来的 `json`。 -=== "config.yml(最新格式)" +::: - ```yaml hl_lines="2 3 78 83" - account: # 账号相关 - uin: 1233456 # QQ账号 - password: '' # 密码为空时使用扫码登录 - encrypt: false # 是否开启密码加密 - status: 0 # 在线状态 请参考 https://github.com/Mrs4s/go-cqhttp/blob/dev/docs/config.md#在线状态 - relogin: # 重连设置 - delay: 3 # 首次重连延迟, 单位秒 - interval: 3 # 重连间隔 - max-times: 0 # 最大重连次数, 0为无限制 - - # 是否使用服务器下发的新地址进行重连 - # 注意, 此设置可能导致在海外服务器上连接情况更差 - use-sso-address: true - - heartbeat: - # 心跳频率, 单位秒 - # -1 为关闭心跳 - interval: 5 - - message: - # 上报数据类型 - # 可选: string,array - post-format: string - # 是否忽略无效的CQ码, 如果为假将原样发送 - ignore-invalid-cqcode: false - # 是否强制分片发送消息 - # 分片发送将会带来更快的速度 - # 但是兼容性会有些问题 - force-fragment: false - # 是否将url分片发送 - fix-url: false - # 下载图片等请求网络代理 - proxy-rewrite: '' - # 是否上报自身消息 - report-self-message: false - # 移除服务端的Reply附带的At - remove-reply-at: false - # 为Reply附加更多信息 - extra-reply-data: false - - output: - # 日志等级 trace,debug,info,warn,error - log-level: warn - # 是否启用 DEBUG - debug: false # 开启调试模式 - - # 默认中间件锚点 - default-middlewares: &default - # 访问密钥, 强烈推荐在公网的服务器设置 - access-token: '' - # 事件过滤器文件目录 - filter: '' - # API限速设置 - # 该设置为全局生效 - # 原 cqhttp 虽然启用了 rate_limit 后缀, 但是基本没插件适配 - # 目前该限速设置为令牌桶算法, 请参考: - # https://baike.baidu.com/item/%E4%BB%A4%E7%89%8C%E6%A1%B6%E7%AE%97%E6%B3%95/6597000?fr=aladdin - rate-limit: - enabled: false # 是否启用限速 - frequency: 1 # 令牌回复频率, 单位秒 - bucket: 1 # 令牌桶大小 - - database: # 数据库相关设置 - leveldb: - # 是否启用内置leveldb数据库 - # 启用将会增加10-20MB的内存占用和一定的磁盘空间 - # 关闭将无法使用 撤回 回复 get_msg 等上下文相关功能 - enable: true - - # 连接服务列表 - servers: - # 添加方式,同一连接方式可添加多个,具体配置说明请查看文档 - #- http: # http 通信 - #- ws: # 正向 Websocket - #- ws-reverse: # 反向 Websocket - #- pprof: #性能分析服务器 - # Zhamao Framework 所需要的服务器配置 - - ws-reverse: - # 是否禁用当前反向WS服务 - disabled: false - # 反向WS Universal 地址 - # 注意 设置了此项地址后下面两项将会被忽略 - universal: ws://127.0.0.1:20001/ - # 反向WS API 地址 - api: ws://your_websocket_api.server - # 反向WS Event 地址 - event: ws://your_websocket_event.server - # 重连间隔 单位毫秒 - reconnect-interval: 3000 - middlewares: - <<: *default # 引用默认中间件 - ``` +config.yml(最新格式): -=== "config.hjson(v1.0.0-beta2或更早版本所用格式)" +```yaml {2,3,78,83} +account: # 账号相关 + uin: 1233456 # QQ账号 + password: '' # 密码为空时使用扫码登录 + encrypt: false # 是否开启密码加密 + status: 0 # 在线状态 请参考 https://github.com/Mrs4s/go-cqhttp/blob/dev/docs/config.md#在线状态 + relogin: # 重连设置 + delay: 3 # 首次重连延迟, 单位秒 + interval: 3 # 重连间隔 + max-times: 0 # 最大重连次数, 0为无限制 - ``` json hl_lines="3 5 81 84" - { - // QQ号 - uin: 你的机器人QQ - // QQ密码 - password: "你的QQ密码" - // 是否启用密码加密 - encrypt_password: false - // 加密后的密码, 如未启用密码加密将为空, 请勿随意修改. - password_encrypted: "" - // 是否启用内置数据库 - // 启用将会增加10-20MB的内存占用和一定的磁盘空间 - // 关闭将无法使用 撤回 回复 get_msg 等上下文相关功能 - enable_db: true - // 访问密钥, 强烈推荐在公网的服务器设置 - access_token: "" - // 重连设置 - relogin: { - // 是否启用自动重连 - // 如不启用掉线后将不会自动重连 - enabled: true - // 重连延迟, 单位秒 - relogin_delay: 3 - // 最大重连次数, 0为无限制 - max_relogin_times: 0 - } - // API限速设置 - // 该设置为全局生效 - // 原 cqhttp 虽然启用了 rate_limit 后缀, 但是基本没插件适配 - // 目前该限速设置为令牌桶算法, 请参考: - //https://baike.baidu.com/item/%E4%BB%A4%E7%89%8C%E6%A1%B6%E7%AE%97%E6%B3%95/6597000?fr=aladdin - _rate_limit: { - // 是否启用限速 - enabled: false - // 令牌回复频率, 单位秒 - frequency: 1 - // 令牌桶大小 - bucket_size: 1 - } - // 是否忽略无效的CQ码 - // 如果为假将原样发送 - ignore_invalid_cqcode: false - // 是否强制分片发送消息 - // 分片发送将会带来更快的速度 - // 但是兼容性会有些问题 - force_fragmented: false - // 心跳频率, 单位秒 - // -1 为关闭心跳 - heartbeat_interval: 0 - // HTTP设置 - http_config: { - // 是否启用正向HTTP服务器 - enabled: true - // 服务端监听地址 - host: 0.0.0.0 - // 服务端监听端口 - port: 5700 - // 反向HTTP超时时间, 单位秒 - // 最小值为5,小于5将会忽略本项设置 - timeout: 0 - // 反向HTTP POST地址列表 - // 格式: - // { - // 地址: secret - // } - post_urls: {} - } - // 正向WS设置 - ws_config: { - // 是否启用正向WS服务器 - enabled: true - // 正向WS服务器监听地址 - host: 0.0.0.0 - // 正向WS服务器监听端口 - port: 6700 - } - // 反向WS设置 - ws_reverse_servers: [ - // 可以添加多个反向WS推送 - { - // 是否启用该推送 - enabled: true - // 反向WS Universal 地址 - // 注意 设置了此项地址后下面两项将会被忽略 - reverse_url: ws://127.0.0.1:20001/ - // 反向WS API 地址 - reverse_api_url: "" - // 反向WS Event 地址 - reverse_event_url: "" - // 重连间隔 单位毫秒 - reverse_reconnect_interval: 3000 - } - ] - // 上报数据类型 - // 可选: string array - post_message_format: string - // 是否使用服务器下发的新地址进行重连 - // 注意, 此设置可能导致在海外服务器上连接情况更差 - use_sso_address: false - // 是否启用 DEBUG - debug: false - // 日志等级 - // WebUi 设置 - web_ui: { - // 是否启用 WebUi - enabled: true - // 监听地址 - host: 127.0.0.1 - // 监听端口 - web_ui_port: 9999 - // 是否接收来自web的输入 - web_input: false - } - } - ``` + # 是否使用服务器下发的新地址进行重连 + # 注意, 此设置可能导致在海外服务器上连接情况更差 + use-sso-address: true -=== "config.json(旧格式)" +heartbeat: +# 心跳频率, 单位秒 +# -1 为关闭心跳 +interval: 5 - ``` json hl_lines="2 3 30 31" - { - "uin": 你的QQ号, - "password": "你的密码", - "encrypt_password": false, - "password_encrypted": "", - "enable_db": true, - "access_token": "", - "relogin": { - "enabled": true, - "relogin_delay": 3, - "max_relogin_times": 0 - }, - "ignore_invalid_cqcode": false, - "force_fragmented": true, - "heartbeat_interval": 0, - "http_config": { - "enabled": false, - "host": "0.0.0.0", - "port": 5700, - "timeout": 0, - "post_urls": {} - }, - "ws_config": { - "enabled": false, - "host": "0.0.0.0", - "port": 6700 - }, - "ws_reverse_servers": [ - { - "enabled": true, - "reverse_url": "ws://127.0.0.1:20001/", - "reverse_api_url": "", - "reverse_event_url": "", - "reverse_reconnect_interval": 3000 - } - ], - "post_message_format": "string", - "debug": false, - "log_level": "" - } - ``` +message: + # 上报数据类型 + # 可选: string,array + post-format: string + # 是否忽略无效的CQ码, 如果为假将原样发送 + ignore-invalid-cqcode: false + # 是否强制分片发送消息 + # 分片发送将会带来更快的速度 + # 但是兼容性会有些问题 + force-fragment: false + # 是否将url分片发送 + fix-url: false + # 下载图片等请求网络代理 + proxy-rewrite: '' + # 是否上报自身消息 + report-self-message: false + # 移除服务端的Reply附带的At + remove-reply-at: false + # 为Reply附加更多信息 + extra-reply-data: false + +output: + # 日志等级 trace,debug,info,warn,error + log-level: warn + # 是否启用 DEBUG + debug: false # 开启调试模式 + +# 默认中间件锚点 +default-middlewares: &default + # 访问密钥, 强烈推荐在公网的服务器设置 + access-token: '' + # 事件过滤器文件目录 + filter: '' + # API限速设置 + # 该设置为全局生效 + # 原 cqhttp 虽然启用了 rate_limit 后缀, 但是基本没插件适配 + # 目前该限速设置为令牌桶算法, 请参考: + # https://baike.baidu.com/item/%E4%BB%A4%E7%89%8C%E6%A1%B6%E7%AE%97%E6%B3%95/6597000?fr=aladdin + rate-limit: + enabled: false # 是否启用限速 + frequency: 1 # 令牌回复频率, 单位秒 + bucket: 1 # 令牌桶大小 + +database: # 数据库相关设置 + leveldb: + # 是否启用内置leveldb数据库 + # 启用将会增加10-20MB的内存占用和一定的磁盘空间 + # 关闭将无法使用 撤回 回复 get_msg 等上下文相关功能 + enable: true + +# 连接服务列表 +servers: + # 添加方式,同一连接方式可添加多个,具体配置说明请查看文档 + #- http: # http 通信 + #- ws: # 正向 Websocket + #- ws-reverse: # 反向 Websocket + #- pprof: #性能分析服务器 + # Zhamao Framework 所需要的服务器配置 + - ws-reverse: + # 是否禁用当前反向WS服务 + disabled: false + # 反向WS Universal 地址 + # 注意 设置了此项地址后下面两项将会被忽略 + universal: ws://127.0.0.1:20001/ + # 反向WS API 地址 + api: ws://your_websocket_api.server + # 反向WS Event 地址 + event: ws://your_websocket_event.server + # 重连间隔 单位毫秒 + reconnect-interval: 3000 + middlewares: + <<: *default # 引用默认中间件 +``` 其中 ws://127.0.0.1:20001/ 中的 127.0.0.1 和 20001 应分别对应炸毛框架配置的 HOST 和 PORT @@ -320,21 +157,19 @@ public function repeat() { 这样,一个简易的复读机就做好了!回到 QQ 机器人聊天,向机器人发送 `echo 你好啊`,它会回复你 `你好啊`。 - -) echo 你好啊 -( 你好啊 -) echo -( 请输入你要回复的内容 -) 哦豁 -( 哦豁 - + > 如果你只回复 `echo` 的话,它会先和你进入一个会话状态,并问你 `请输入你要回复的内容`,这时你再次说一些内容例如 `哦豁`,会回复你 `哦豁`。效果和直接输入 `echo 哦豁` 是一致的,这是炸毛框架内的一个封装好的命令参数对话询问功能。有关参数询问功能,请看后面的进阶模块。 - - ## 使用机器人 API 和事件 如果你想不只是回复消息,还要做其他复杂的动作(Action),使用 OneBot Action(又名 OneBot API)进行发送即可,见 [机器人 API](/component/bot/robot-api)。 -如果想处理其他类型的事件,比如 QQ 群通知事件等,见 [机器人注解事件](/event/robot-annotations/)。 \ No newline at end of file +如果想处理其他类型的事件,比如 QQ 群通知事件等,见 [机器人注解事件](/event/robot-annotations/)。 diff --git a/docs/guide/register-event.md b/docs/guide/register-event.md index 80cc0374..6469b5a8 100644 --- a/docs/guide/register-event.md +++ b/docs/guide/register-event.md @@ -39,8 +39,11 @@ class Weather { } ``` -!!! note "提示" - 为了简单起见,我们在这里的例子中没有接入真实的天气数据,但要接入也非常简单,你可以使用中国天气网、和风天气等网站提供的 API,本教程的进阶一栏后期会详细编写如何对接一个天气 API 接口。 +::: tip 提示 + +为了简单起见,我们在这里的例子中没有接入真实的天气数据,但要接入也非常简单,你可以使用中国天气网、和风天气等网站提供的 API,本教程的进阶一栏后期会详细编写如何对接一个天气 API 接口。 + +::: 在上方代码编写完毕后,运行框架(或运行过程中终端输入 `reload`),然后使用机器人客户端连接到炸毛框架,即可实现我们的第一个功能。我们在代码中编写了一个**注解事件**:`@CQCommand`,此注解事件是用于接收用户的普通消息并切分成各类命令规则的一个注解事件绑定。代码中的注解事件省去了注解中的键名,也可以写作 `@CQCommand(match="天气")`。 @@ -56,11 +59,11 @@ class Weather { 最后,函数直接返回了一个字符串,作为事件的响应,炸毛框架会自动处理并调用机器人客户端的接口,最后返回给用户消息。这里也可以使用上下文的 `ctx()->reply("xxx")` 方法替代,不返回字符串。注意两者只能选择一种方式,取决于开发者的开发习惯。 - -) 天气 北京 -( 北京 天气情况:2020年12月22日,晴,-2~9℃ blablabla -) 天气 -( 请告诉我你要查询的城市 -) 北京 -( 北京 天气情况:2020年12月22日,晴,-2~9℃ blablabla - + diff --git a/docs/guide/upgrade.md b/docs/guide/upgrade.md index 72d1bc81..36b51208 100644 --- a/docs/guide/upgrade.md +++ b/docs/guide/upgrade.md @@ -34,4 +34,4 @@ Phar 打包模式更新则必须重新自行打包新版本,例如从 Composer ## 升级提示 -如果在升级过程中遇到了提示,则可能是需要升级某些配置文件需要手动进行合并更新。如果提示了更新,建议到 `vendor/zhamao/framework/config/global.php` 框架的最新库内配置文件与 `config/global.php` 文件进行对比。 \ No newline at end of file +如果在升级过程中遇到了提示,则可能是需要升级某些配置文件需要手动进行合并更新。如果提示了更新,建议到 `vendor/zhamao/framework/config/global.php` 框架的最新库内配置文件与 `config/global.php` 文件进行对比。 diff --git a/docs/guide/write-module.md b/docs/guide/write-module.md index d681d7d8..a4eb47e8 100644 --- a/docs/guide/write-module.md +++ b/docs/guide/write-module.md @@ -6,7 +6,7 @@ 框架默认使用脚手架构建好后,目录结构大致为下面这样: -```bash +``` zhamao-framework-starter/ ├── config/ # 项目的配置文件文件夹,如 global.php ├── src/ # 项目的主要源码目录 @@ -28,20 +28,28 @@ namespace Module\; class ModuleA {} ``` -!!! fail "警告" - 如果没有遵守上方的类和文件命名规则的话(文件名、文件夹名和命名空间的统一性),在加载框架时就会报错,无法找到对应的类。因为框架的注解解析依赖于 Composer 中 psr-4 规则的自动加载。 +::: danger 警告 + +如果没有遵守上方的类和文件命名规则的话(文件名、文件夹名和命名空间的统一性),在加载框架时就会报错,无法找到对应的类。因为框架的注解解析依赖于 Composer 中 psr-4 规则的自动加载。 + +::: ## 创建模块 + ### 标准形式 + 我们这里以 `Entertain` 娱乐模块的创建为例,新建一个内有 `Dice.php` 掷骰子功能的模块,目录结构如下,在 `Module/` 下新建文件夹 `Entertain/`,再在此子目录下新建 `Dice.php` 文件。 -```bash + +``` zhamao-framework-starter/ └── src/ └── Module/ └── Entertain/ └── Dice.php ``` + 新建的 PHP 文件按照如下方式编写: + ```php reply("你好,我是炸毛!"); -} -/** - * @RequestMapping("/index") - */ -public function index() { - return "

hello!

"; -} -``` - - - -## 开始前 - -首先,你需要了解你需要知道哪些事情才能开始着手使用框架: - -1. Linux 命令行(会跑 Linux 程序) -2. php >=7.2 开发环境(项目会持续支持最新的 PHP 版本) -4. OneBot 机器人聊天接口标准 - -需要值得注意的是,本教程中所涉及的内容均为尽可能翻译为白话的方式进行描述,但对于框架的组件或事件等需要单独拆分说明文档的部分则需要足够详细,所以本教程提供一个快速上手的教程,并且会将最典型的安装方式写到快速教程篇。 - -!!! bug "文档提示" - - 此文档采用 MkDocs 驱动,文档的搜索组件原生不支持中文搜索,且分词很难控制,所以搜索体验会大打折扣,敬请谅解!搜不到不是没这个东西,建议这种情况可以自行翻阅目录查看! - - -## 框架特色 - -- 支持MySQL数据库(连接池),自带查询缓存提高多查询时的效率 -- Websocket 服务器、HTTP 服务器兼容运行,一个框架多个用处 -- 支持命令、自然语言处理等多种插件形式 -- 支持多个机器人账号负载均衡 -- 协程 + TaskWorker 进程重度任务处理机制,保证高效,单个请求响应时间为 0.1 ms 左右 -- 模块分离和自由组合,可根据自身需求自己建立模块内的目录结构和代码结构 -- 灵活的注释注解注册事件方式,弥补 PHP 语言缺少注解的遗憾 - -## 文档主题 - -### 主题 - -
- - -
- - - -### 主色调 - -
- - - - - - - - - - - - - - - - - - - - - -
- -### 辅色调 - -
- - - - diff --git a/docs/javascripts/config.js b/docs/javascripts/config.js deleted file mode 100644 index 7e1faeb6..00000000 --- a/docs/javascripts/config.js +++ /dev/null @@ -1,97 +0,0 @@ -hljs.initHighlighting() - -var _hmt = _hmt || []; -(function () { - var hm = document.createElement("script"); - hm.src = "https://hm.baidu.com/hm.js?f0f276cefa10aa31a20ae3815a50b795"; - var s = document.getElementsByTagName("script")[0]; - s.parentNode.insertBefore(hm, s); -})(); - -function appendChatModule(id, chatDialogs) { - let insertDiv = document.getElementById(id); - let ss = ''; - ss += '
'; - for(let i of chatDialogs) { - if (i.role === 0) { - ss += '
\n' + - ' \n' + - '
' + i.msg + '
\n' + - '
'; - } else { - ss += '
\n' + - '
' + i.msg + '
\n' + - ' \n' + - '
'; - } - } - insertDiv.innerHTML = ss + '
'; -} - -function getCookie(name) { - var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)"); - - if (arr = document.cookie.match(reg)) - - return unescape(arr[2]); - else - return null; -} - -function setCookie(name, value) { - var Days = 30; - var exp = new Date(); - exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000); - document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString() + ";path=/"; -} - -s_theme=getCookie("_theme"); -if(s_theme !== undefined) { - document.body.setAttribute("data-md-color-scheme", s_theme) - var name = document.querySelector("#__code_0 code span:nth-child(7)") - name.textContent = s_theme -} - -s_primary=getCookie("_primary_color"); -if(s_primary !== null) { - document.body.setAttribute("data-md-color-primary", s_primary); - var name2 = document.querySelector("#__code_2 code span:nth-child(7)"); - if (s_primary !== null && name2 !== null) name2.textContent = s_primary.replace("-", " "); -} - -s_accent=getCookie("_accent_color"); -if(s_accent !== null) { - document.body.setAttribute("data-md-color-accent", s_accent); - var name3 = document.querySelector("#__code_3 code span:nth-child(7)"); - if (s_accent !== null && name3 !== null) name3.textContent = s_accent.replace("-", " "); -} - -setTimeout(() => { - let ls = document.querySelectorAll("chat-box"); - for(let i of ls) { - let final = '
'; - let dialogs = i.innerHTML.split("\n"); - for(let j of dialogs) { - if(j === '') continue; - if(j.substr(0, 2) === ') ') { - final += '
\n' + - '
' + j.substr(2).replaceAll("\\n", "
") + '
\n' + - ' \n' + - '
'; - } else if (j.substr(0, 2) === '( ') { - final += '
\n' + - ' \n' + - '
' + j.substr(2).replaceAll("\\n", "
") + '
\n' + - '
'; - } else if (j.substr(0, 2) === '^ ') { - final += '
' + j.substr(2) + '
'; - } else if (j.substr(0, 2) === '[ ') { - final += '
\n' + - ' \n' + - '
\n' + - '
'; - } - } - i.innerHTML = final; - } -}, 500); diff --git a/docs/javascripts/library/highlight.min.js b/docs/javascripts/library/highlight.min.js deleted file mode 100644 index ea8775a3..00000000 --- a/docs/javascripts/library/highlight.min.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - Highlight.js 10.1.1 (93fd0d73) - License: BSD-3-Clause - Copyright (c) 2006-2020, Ivan Sagalaev -*/ -var hljs=function(){"use strict";function e(n){Object.freeze(n);var t="function"==typeof n;return Object.getOwnPropertyNames(n).forEach((function(r){!Object.hasOwnProperty.call(n,r)||null===n[r]||"object"!=typeof n[r]&&"function"!=typeof n[r]||t&&("caller"===r||"callee"===r||"arguments"===r)||Object.isFrozen(n[r])||e(n[r])})),n}class n{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data}ignoreMatch(){this.ignore=!0}}function t(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function r(e,...n){var t={};for(const n in e)t[n]=e[n];return n.forEach((function(e){for(const n in e)t[n]=e[n]})),t}function a(e){return e.nodeName.toLowerCase()}var i=Object.freeze({__proto__:null,escapeHTML:t,inherit:r,nodeStream:function(e){var n=[];return function e(t,r){for(var i=t.firstChild;i;i=i.nextSibling)3===i.nodeType?r+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:r,node:i}),r=e(i,r),a(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:r,node:i}));return r}(e,0),n},mergeStreams:function(e,n,r){var i=0,s="",o=[];function l(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset"}function u(e){s+=""}function d(e){("start"===e.event?c:u)(e.node)}for(;e.length||n.length;){var g=l();if(s+=t(r.substring(i,g[0].offset)),i=g[0].offset,g===e){o.reverse().forEach(u);do{d(g.splice(0,1)[0]),g=l()}while(g===e&&g.length&&g[0].offset===i);o.reverse().forEach(c)}else"start"===g[0].event?o.push(g[0].node):o.pop(),d(g.splice(0,1)[0])}return s+t(r.substr(i))}});const s="",o=e=>!!e.kind;class l{constructor(e,n){this.buffer="",this.classPrefix=n.classPrefix,e.walk(this)}addText(e){this.buffer+=t(e)}openNode(e){if(!o(e))return;let n=e.kind;e.sublanguage||(n=`${this.classPrefix}${n}`),this.span(n)}closeNode(e){o(e)&&(this.buffer+=s)}value(){return this.buffer}span(e){this.buffer+=``}}class c{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const n={kind:e,children:[]};this.add(n),this.stack.push(n)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,n){return"string"==typeof n?e.addText(n):n.children&&(e.openNode(n),n.children.forEach(n=>this._walk(e,n)),e.closeNode(n)),e}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every(e=>"string"==typeof e)?e.children=[e.children.join("")]:e.children.forEach(e=>{c._collapse(e)}))}}class u extends c{constructor(e){super(),this.options=e}addKeyword(e,n){""!==e&&(this.openNode(n),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,n){const t=e.root;t.kind=n,t.sublanguage=!0,this.add(t)}toHTML(){return new l(this,this.options).value()}finalize(){return!0}}function d(e){return e?"string"==typeof e?e:e.source:null}const g="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",h={begin:"\\\\[\\s\\S]",relevance:0},f={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[h]},p={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[h]},b={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},m=function(e,n,t={}){var a=r({className:"comment",begin:e,end:n,contains:[]},t);return a.contains.push(b),a.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),a},v=m("//","$"),x=m("/\\*","\\*/"),E=m("#","$");var _=Object.freeze({__proto__:null,IDENT_RE:"[a-zA-Z]\\w*",UNDERSCORE_IDENT_RE:"[a-zA-Z_]\\w*",NUMBER_RE:"\\b\\d+(\\.\\d+)?",C_NUMBER_RE:g,BINARY_NUMBER_RE:"\\b(0b[01]+)",RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const n=/^#![ ]*\//;return e.binary&&(e.begin=function(...e){return e.map(e=>d(e)).join("")}(n,/.*\b/,e.binary,/\b.*/)),r({className:"meta",begin:n,end:/$/,relevance:0,"on:begin":(e,n)=>{0!==e.index&&n.ignoreMatch()}},e)},BACKSLASH_ESCAPE:h,APOS_STRING_MODE:f,QUOTE_STRING_MODE:p,PHRASAL_WORDS_MODE:b,COMMENT:m,C_LINE_COMMENT_MODE:v,C_BLOCK_COMMENT_MODE:x,HASH_COMMENT_MODE:E,NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?",relevance:0},C_NUMBER_MODE:{className:"number",begin:g,relevance:0},BINARY_NUMBER_MODE:{className:"number",begin:"\\b(0b[01]+)",relevance:0},CSS_NUMBER_MODE:{className:"number",begin:"\\b\\d+(\\.\\d+)?(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},REGEXP_MODE:{begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[h,{begin:/\[/,end:/\]/,relevance:0,contains:[h]}]}]},TITLE_MODE:{className:"title",begin:"[a-zA-Z]\\w*",relevance:0},UNDERSCORE_TITLE_MODE:{className:"title",begin:"[a-zA-Z_]\\w*",relevance:0},METHOD_GUARD:{begin:"\\.\\s*[a-zA-Z_]\\w*",relevance:0},END_SAME_AS_BEGIN:function(e){return Object.assign(e,{"on:begin":(e,n)=>{n.data._beginMatch=e[1]},"on:end":(e,n)=>{n.data._beginMatch!==e[1]&&n.ignoreMatch()}})}}),N="of and for in not or if then".split(" ");function w(e,n){return n?+n:function(e){return N.includes(e.toLowerCase())}(e)?0:1}const R=t,y=r,{nodeStream:k,mergeStreams:O}=i,M=Symbol("nomatch");return function(t){var a=[],i={},s={},o=[],l=!0,c=/(^(<[^>]+>|\t|)+|\n)/gm,g="Could not find the language '{}', did you forget to load/include a language module?";const h={disableAutodetect:!0,name:"Plain text",contains:[]};var f={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:null,__emitter:u};function p(e){return f.noHighlightRe.test(e)}function b(e,n,t,r){var a={code:n,language:e};S("before:highlight",a);var i=a.result?a.result:m(a.language,a.code,t,r);return i.code=a.code,S("after:highlight",i),i}function m(e,t,a,s){var o=t;function c(e,n){var t=E.case_insensitive?n[0].toLowerCase():n[0];return Object.prototype.hasOwnProperty.call(e.keywords,t)&&e.keywords[t]}function u(){null!=y.subLanguage?function(){if(""!==A){var e=null;if("string"==typeof y.subLanguage){if(!i[y.subLanguage])return void O.addText(A);e=m(y.subLanguage,A,!0,k[y.subLanguage]),k[y.subLanguage]=e.top}else e=v(A,y.subLanguage.length?y.subLanguage:null);y.relevance>0&&(I+=e.relevance),O.addSublanguage(e.emitter,e.language)}}():function(){if(!y.keywords)return void O.addText(A);let e=0;y.keywordPatternRe.lastIndex=0;let n=y.keywordPatternRe.exec(A),t="";for(;n;){t+=A.substring(e,n.index);const r=c(y,n);if(r){const[e,a]=r;O.addText(t),t="",I+=a,O.addKeyword(n[0],e)}else t+=n[0];e=y.keywordPatternRe.lastIndex,n=y.keywordPatternRe.exec(A)}t+=A.substr(e),O.addText(t)}(),A=""}function h(e){return e.className&&O.openNode(e.className),y=Object.create(e,{parent:{value:y}})}function p(e){return 0===y.matcher.regexIndex?(A+=e[0],1):(L=!0,0)}var b={};function x(t,r){var i=r&&r[0];if(A+=t,null==i)return u(),0;if("begin"===b.type&&"end"===r.type&&b.index===r.index&&""===i){if(A+=o.slice(r.index,r.index+1),!l){const n=Error("0 width match regex");throw n.languageName=e,n.badRule=b.rule,n}return 1}if(b=r,"begin"===r.type)return function(e){var t=e[0],r=e.rule;const a=new n(r),i=[r.__beforeBegin,r["on:begin"]];for(const n of i)if(n&&(n(e,a),a.ignore))return p(t);return r&&r.endSameAsBegin&&(r.endRe=RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")),r.skip?A+=t:(r.excludeBegin&&(A+=t),u(),r.returnBegin||r.excludeBegin||(A=t)),h(r),r.returnBegin?0:t.length}(r);if("illegal"===r.type&&!a){const e=Error('Illegal lexeme "'+i+'" for mode "'+(y.className||"")+'"');throw e.mode=y,e}if("end"===r.type){var s=function(e){var t=e[0],r=o.substr(e.index),a=function e(t,r,a){let i=function(e,n){var t=e&&e.exec(n);return t&&0===t.index}(t.endRe,a);if(i){if(t["on:end"]){const e=new n(t);t["on:end"](r,e),e.ignore&&(i=!1)}if(i){for(;t.endsParent&&t.parent;)t=t.parent;return t}}if(t.endsWithParent)return e(t.parent,r,a)}(y,e,r);if(!a)return M;var i=y;i.skip?A+=t:(i.returnEnd||i.excludeEnd||(A+=t),u(),i.excludeEnd&&(A=t));do{y.className&&O.closeNode(),y.skip||y.subLanguage||(I+=y.relevance),y=y.parent}while(y!==a.parent);return a.starts&&(a.endSameAsBegin&&(a.starts.endRe=a.endRe),h(a.starts)),i.returnEnd?0:t.length}(r);if(s!==M)return s}if("illegal"===r.type&&""===i)return 1;if(B>1e5&&B>3*r.index)throw Error("potential infinite loop, way more iterations than matches");return A+=i,i.length}var E=T(e);if(!E)throw console.error(g.replace("{}",e)),Error('Unknown language: "'+e+'"');var _=function(e){function n(n,t){return RegExp(d(n),"m"+(e.case_insensitive?"i":"")+(t?"g":""))}class t{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,n){n.position=this.position++,this.matchIndexes[this.matchAt]=n,this.regexes.push([n,e]),this.matchAt+=function(e){return RegExp(e.toString()+"|").exec("").length-1}(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map(e=>e[1]);this.matcherRe=n(function(e,n="|"){for(var t=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,r=0,a="",i=0;i0&&(a+=n),a+="(";o.length>0;){var l=t.exec(o);if(null==l){a+=o;break}a+=o.substring(0,l.index),o=o.substring(l.index+l[0].length),"\\"===l[0][0]&&l[1]?a+="\\"+(+l[1]+s):(a+=l[0],"("===l[0]&&r++)}a+=")"}return a}(e),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const n=this.matcherRe.exec(e);if(!n)return null;const t=n.findIndex((e,n)=>n>0&&void 0!==e),r=this.matchIndexes[t];return n.splice(0,t),Object.assign(n,r)}}class a{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const n=new t;return this.rules.slice(e).forEach(([e,t])=>n.addRule(e,t)),n.compile(),this.multiRegexes[e]=n,n}considerAll(){this.regexIndex=0}addRule(e,n){this.rules.push([e,n]),"begin"===n.type&&this.count++}exec(e){const n=this.getMatcher(this.regexIndex);n.lastIndex=this.lastIndex;const t=n.exec(e);return t&&(this.regexIndex+=t.position+1,this.regexIndex===this.count&&(this.regexIndex=0)),t}}function i(e,n){const t=e.input[e.index-1],r=e.input[e.index+e[0].length];"."!==t&&"."!==r||n.ignoreMatch()}if(e.contains&&e.contains.includes("self"))throw Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return function t(s,o){const l=s;if(s.compiled)return l;s.compiled=!0,s.__beforeBegin=null,s.keywords=s.keywords||s.beginKeywords;let c=null;if("object"==typeof s.keywords&&(c=s.keywords.$pattern,delete s.keywords.$pattern),s.keywords&&(s.keywords=function(e,n){var t={};return"string"==typeof e?r("keyword",e):Object.keys(e).forEach((function(n){r(n,e[n])})),t;function r(e,r){n&&(r=r.toLowerCase()),r.split(" ").forEach((function(n){var r=n.split("|");t[r[0]]=[e,w(r[0],r[1])]}))}}(s.keywords,e.case_insensitive)),s.lexemes&&c)throw Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");return l.keywordPatternRe=n(s.lexemes||c||/\w+/,!0),o&&(s.beginKeywords&&(s.begin="\\b("+s.beginKeywords.split(" ").join("|")+")(?=\\b|\\s)",s.__beforeBegin=i),s.begin||(s.begin=/\B|\b/),l.beginRe=n(s.begin),s.endSameAsBegin&&(s.end=s.begin),s.end||s.endsWithParent||(s.end=/\B|\b/),s.end&&(l.endRe=n(s.end)),l.terminator_end=d(s.end)||"",s.endsWithParent&&o.terminator_end&&(l.terminator_end+=(s.end?"|":"")+o.terminator_end)),s.illegal&&(l.illegalRe=n(s.illegal)),void 0===s.relevance&&(s.relevance=1),s.contains||(s.contains=[]),s.contains=[].concat(...s.contains.map((function(e){return function(e){return e.variants&&!e.cached_variants&&(e.cached_variants=e.variants.map((function(n){return r(e,{variants:null},n)}))),e.cached_variants?e.cached_variants:function e(n){return!!n&&(n.endsWithParent||e(n.starts))}(e)?r(e,{starts:e.starts?r(e.starts):null}):Object.isFrozen(e)?r(e):e}("self"===e?s:e)}))),s.contains.forEach((function(e){t(e,l)})),s.starts&&t(s.starts,o),l.matcher=function(e){const n=new a;return e.contains.forEach(e=>n.addRule(e.begin,{rule:e,type:"begin"})),e.terminator_end&&n.addRule(e.terminator_end,{type:"end"}),e.illegal&&n.addRule(e.illegal,{type:"illegal"}),n}(l),l}(e)}(E),N="",y=s||_,k={},O=new f.__emitter(f);!function(){for(var e=[],n=y;n!==E;n=n.parent)n.className&&e.unshift(n.className);e.forEach(e=>O.openNode(e))}();var A="",I=0,S=0,B=0,L=!1;try{for(y.matcher.considerAll();;){B++,L?L=!1:(y.matcher.lastIndex=S,y.matcher.considerAll());const e=y.matcher.exec(o);if(!e)break;const n=x(o.substring(S,e.index),e);S=e.index+n}return x(o.substr(S)),O.closeAllNodes(),O.finalize(),N=O.toHTML(),{relevance:I,value:N,language:e,illegal:!1,emitter:O,top:y}}catch(n){if(n.message&&n.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:n.message,context:o.slice(S-100,S+100),mode:n.mode},sofar:N,relevance:0,value:R(o),emitter:O};if(l)return{illegal:!1,relevance:0,value:R(o),emitter:O,language:e,top:y,errorRaised:n};throw n}}function v(e,n){n=n||f.languages||Object.keys(i);var t=function(e){const n={relevance:0,emitter:new f.__emitter(f),value:R(e),illegal:!1,top:h};return n.emitter.addText(e),n}(e),r=t;return n.filter(T).filter(I).forEach((function(n){var a=m(n,e,!1);a.language=n,a.relevance>r.relevance&&(r=a),a.relevance>t.relevance&&(r=t,t=a)})),r.language&&(t.second_best=r),t}function x(e){return f.tabReplace||f.useBR?e.replace(c,e=>"\n"===e?f.useBR?"
":e:f.tabReplace?e.replace(/\t/g,f.tabReplace):e):e}function E(e){let n=null;const t=function(e){var n=e.className+" ";n+=e.parentNode?e.parentNode.className:"";const t=f.languageDetectRe.exec(n);if(t){var r=T(t[1]);return r||(console.warn(g.replace("{}",t[1])),console.warn("Falling back to no-highlight mode for this block.",e)),r?t[1]:"no-highlight"}return n.split(/\s+/).find(e=>p(e)||T(e))}(e);if(p(t))return;S("before:highlightBlock",{block:e,language:t}),f.useBR?(n=document.createElement("div")).innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n"):n=e;const r=n.textContent,a=t?b(t,r,!0):v(r),i=k(n);if(i.length){const e=document.createElement("div");e.innerHTML=a.value,a.value=O(i,k(e),r)}a.value=x(a.value),S("after:highlightBlock",{block:e,result:a}),e.innerHTML=a.value,e.className=function(e,n,t){var r=n?s[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),e.includes(r)||a.push(r),a.join(" ").trim()}(e.className,t,a.language),e.result={language:a.language,re:a.relevance,relavance:a.relevance},a.second_best&&(e.second_best={language:a.second_best.language,re:a.second_best.relevance,relavance:a.second_best.relevance})}const N=()=>{if(!N.called){N.called=!0;var e=document.querySelectorAll("pre code");a.forEach.call(e,E)}};function T(e){return e=(e||"").toLowerCase(),i[e]||i[s[e]]}function A(e,{languageName:n}){"string"==typeof e&&(e=[e]),e.forEach(e=>{s[e]=n})}function I(e){var n=T(e);return n&&!n.disableAutodetect}function S(e,n){var t=e;o.forEach((function(e){e[t]&&e[t](n)}))}Object.assign(t,{highlight:b,highlightAuto:v,fixMarkup:x,highlightBlock:E,configure:function(e){f=y(f,e)},initHighlighting:N,initHighlightingOnLoad:function(){window.addEventListener("DOMContentLoaded",N,!1)},registerLanguage:function(e,n){var r=null;try{r=n(t)}catch(n){if(console.error("Language definition for '{}' could not be registered.".replace("{}",e)),!l)throw n;console.error(n),r=h}r.name||(r.name=e),i[e]=r,r.rawDefinition=n.bind(null,t),r.aliases&&A(r.aliases,{languageName:e})},listLanguages:function(){return Object.keys(i)},getLanguage:T,registerAliases:A,requireLanguage:function(e){var n=T(e);if(n)return n;throw Error("The '{}' language is required, but not loaded.".replace("{}",e))},autoDetection:I,inherit:y,addPlugin:function(e){o.push(e)}}),t.debugMode=function(){l=!1},t.safeMode=function(){l=!0},t.versionString="10.1.1";for(const n in _)"object"==typeof _[n]&&e(_[n]);return Object.assign(t,_),t}({})}();"object"==typeof exports&&"undefined"!=typeof module&&(module.exports=hljs); -hljs.registerLanguage("apache",function(){"use strict";return function(e){var n={className:"number",begin:"\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?"};return{name:"Apache config",aliases:["apacheconf"],case_insensitive:!0,contains:[e.HASH_COMMENT_MODE,{className:"section",begin:"",contains:[n,{className:"number",begin:":\\d{1,5}"},e.inherit(e.QUOTE_STRING_MODE,{relevance:0})]},{className:"attribute",begin:/\w+/,relevance:0,keywords:{nomarkup:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{end:/$/,relevance:0,keywords:{literal:"on off all deny allow"},contains:[{className:"meta",begin:"\\s\\[",end:"\\]$"},{className:"variable",begin:"[\\$%]\\{",end:"\\}",contains:["self",{className:"number",begin:"[\\$%]\\d+"}]},n,{className:"number",begin:"\\d+"},e.QUOTE_STRING_MODE]}}],illegal:/\S/}}}()); -hljs.registerLanguage("bash",function(){"use strict";return function(e){const s={};Object.assign(s,{className:"variable",variants:[{begin:/\$[\w\d#@][\w\d_]*/},{begin:/\$\{/,end:/\}/,contains:[{begin:/:-/,contains:[s]}]}]});const t={className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},n={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,t]};t.contains.push(n);const a={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,s]},i=e.SHEBANG({binary:"(fish|bash|zsh|sh|csh|ksh|tcsh|dash|scsh)",relevance:10}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b-?[a-z\._]+\b/,keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},contains:[i,e.SHEBANG(),c,a,e.HASH_COMMENT_MODE,n,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},s]}}}()); -hljs.registerLanguage("c-like",function(){"use strict";return function(e){function t(e){return"(?:"+e+")?"}var n="(decltype\\(auto\\)|"+t("[a-zA-Z_]\\w*::")+"[a-zA-Z_]\\w*"+t("<.*?>")+")",r={className:"keyword",begin:"\\b[a-z\\d_]*_t\\b"},a={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'(\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)|.)",end:"'",illegal:"."},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},i={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{"meta-keyword":"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(a,{className:"meta-string"}),{className:"meta-string",begin:/<.*?>/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},o={className:"title",begin:t("[a-zA-Z_]\\w*::")+e.IDENT_RE,relevance:0},c=t("[a-zA-Z_]\\w*::")+e.IDENT_RE+"\\s*\\(",l={keyword:"int float while private char char8_t char16_t char32_t catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid wchar_t short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignas alignof constexpr consteval constinit decltype concept co_await co_return co_yield requires noexcept static_assert thread_local restrict final override atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq",built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr _Bool complex _Complex imaginary _Imaginary",literal:"true false nullptr NULL"},d=[r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,i,a],_={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:l,contains:d.concat([{begin:/\(/,end:/\)/,keywords:l,contains:d.concat(["self"]),relevance:0}]),relevance:0},u={className:"function",begin:"("+n+"[\\*&\\s]+)+"+c,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:l,illegal:/[^\w\s\*&:<>]/,contains:[{begin:"decltype\\(auto\\)",keywords:l,relevance:0},{begin:c,returnBegin:!0,contains:[o],relevance:0},{className:"params",begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r,{begin:/\(/,end:/\)/,keywords:l,relevance:0,contains:["self",e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,a,i,r]}]},r,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,s]};return{aliases:["c","cc","h","c++","h++","hpp","hh","hxx","cxx"],keywords:l,disableAutodetect:!0,illegal:"",keywords:l,contains:["self",r]},{begin:e.IDENT_RE+"::",keywords:l},{className:"class",beginKeywords:"class struct",end:/[{;:]/,contains:[{begin://,contains:["self"]},e.TITLE_MODE]}]),exports:{preprocessor:s,strings:a,keywords:l}}}}()); -hljs.registerLanguage("c",function(){"use strict";return function(e){var n=e.getLanguage("c-like").rawDefinition();return n.name="C",n.aliases=["c","h"],n}}()); -hljs.registerLanguage("coffeescript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);return function(r){var t={keyword:e.concat(["then","unless","until","loop","by","when","and","or","is","isnt","not"]).filter((e=>n=>!e.includes(n))(["var","const","let","function","static"])).join(" "),literal:n.concat(["yes","no","on","off"]).join(" "),built_in:a.concat(["npm","print"]).join(" ")},i="[A-Za-z$_][0-9A-Za-z$_]*",s={className:"subst",begin:/#\{/,end:/}/,keywords:t},o=[r.BINARY_NUMBER_MODE,r.inherit(r.C_NUMBER_MODE,{starts:{end:"(\\s*/)?",relevance:0}}),{className:"string",variants:[{begin:/'''/,end:/'''/,contains:[r.BACKSLASH_ESCAPE]},{begin:/'/,end:/'/,contains:[r.BACKSLASH_ESCAPE]},{begin:/"""/,end:/"""/,contains:[r.BACKSLASH_ESCAPE,s]},{begin:/"/,end:/"/,contains:[r.BACKSLASH_ESCAPE,s]}]},{className:"regexp",variants:[{begin:"///",end:"///",contains:[s,r.HASH_COMMENT_MODE]},{begin:"//[gim]{0,3}(?=\\W)",relevance:0},{begin:/\/(?![ *]).*?(?![\\]).\/[gim]{0,3}(?=\W)/}]},{begin:"@"+i},{subLanguage:"javascript",excludeBegin:!0,excludeEnd:!0,variants:[{begin:"```",end:"```"},{begin:"`",end:"`"}]}];s.contains=o;var c=r.inherit(r.TITLE_MODE,{begin:i}),l={className:"params",begin:"\\([^\\(]",returnBegin:!0,contains:[{begin:/\(/,end:/\)/,keywords:t,contains:["self"].concat(o)}]};return{name:"CoffeeScript",aliases:["coffee","cson","iced"],keywords:t,illegal:/\/\*/,contains:o.concat([r.COMMENT("###","###"),r.HASH_COMMENT_MODE,{className:"function",begin:"^\\s*"+i+"\\s*=\\s*(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[c,l]},{begin:/[:\(,=]\s*/,relevance:0,contains:[{className:"function",begin:"(\\(.*\\))?\\s*\\B[-=]>",end:"[-=]>",returnBegin:!0,contains:[l]}]},{className:"class",beginKeywords:"class",end:"$",illegal:/[:="\[\]]/,contains:[{beginKeywords:"extends",endsWithParent:!0,illegal:/[:="\[\]]/,contains:[c]},c]},{begin:i+":",end:":",returnBegin:!0,returnEnd:!0,relevance:0}])}}}()); -hljs.registerLanguage("cpp",function(){"use strict";return function(e){var t=e.getLanguage("c-like").rawDefinition();return t.disableAutodetect=!1,t.name="C++",t.aliases=["cc","c++","h++","hpp","hh","hxx","cxx"],t}}()); -hljs.registerLanguage("csharp",function(){"use strict";return function(e){var n={keyword:"abstract as base bool break byte case catch char checked const continue decimal default delegate do double enum event explicit extern finally fixed float for foreach goto if implicit in int interface internal is lock long object operator out override params private protected public readonly ref sbyte sealed short sizeof stackalloc static string struct switch this try typeof uint ulong unchecked unsafe ushort using virtual void volatile while add alias ascending async await by descending dynamic equals from get global group into join let nameof on orderby partial remove select set value var when where yield",literal:"null false true"},i=e.inherit(e.TITLE_MODE,{begin:"[a-zA-Z](\\.?\\w)*"}),a={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},s={className:"string",begin:'@"',end:'"',contains:[{begin:'""'}]},t=e.inherit(s,{illegal:/\n/}),l={className:"subst",begin:"{",end:"}",keywords:n},r=e.inherit(l,{illegal:/\n/}),c={className:"string",begin:/\$"/,end:'"',illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},e.BACKSLASH_ESCAPE,r]},o={className:"string",begin:/\$@"/,end:'"',contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},l]},g=e.inherit(o,{illegal:/\n/,contains:[{begin:"{{"},{begin:"}}"},{begin:'""'},r]});l.contains=[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.C_BLOCK_COMMENT_MODE],r.contains=[g,c,t,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,a,e.inherit(e.C_BLOCK_COMMENT_MODE,{illegal:/\n/})];var d={variants:[o,c,s,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},E={begin:"<",end:">",contains:[{beginKeywords:"in out"},i]},_=e.IDENT_RE+"(<"+e.IDENT_RE+"(\\s*,\\s*"+e.IDENT_RE+")*>)?(\\[\\])?",b={begin:"@"+e.IDENT_RE,relevance:0};return{name:"C#",aliases:["cs","c#"],keywords:n,illegal:/::/,contains:[e.COMMENT("///","$",{returnBegin:!0,contains:[{className:"doctag",variants:[{begin:"///",relevance:0},{begin:"\x3c!--|--\x3e"},{begin:""}]}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"meta",begin:"#",end:"$",keywords:{"meta-keyword":"if else elif endif define undef warning error line region endregion pragma checksum"}},d,a,{beginKeywords:"class interface",end:/[{;=]/,illegal:/[^\s:,]/,contains:[{beginKeywords:"where class"},i,E,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"namespace",end:/[{;=]/,illegal:/[^\s:]/,contains:[i,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"meta",begin:"^\\s*\\[",excludeBegin:!0,end:"\\]",excludeEnd:!0,contains:[{className:"meta-string",begin:/"/,end:/"/}]},{beginKeywords:"new return throw await else",relevance:0},{className:"function",begin:"("+_+"\\s+)+"+e.IDENT_RE+"\\s*(\\<.+\\>)?\\s*\\(",returnBegin:!0,end:/\s*[{;=]/,excludeEnd:!0,keywords:n,contains:[{begin:e.IDENT_RE+"\\s*(\\<.+\\>)?\\s*\\(",returnBegin:!0,contains:[e.TITLE_MODE,E],relevance:0},{className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:n,relevance:0,contains:[d,a,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},b]}}}()); -hljs.registerLanguage("css",function(){"use strict";return function(e){var n={begin:/(?:[A-Z\_\.\-]+|--[a-zA-Z0-9_-]+)\s*:/,returnBegin:!0,end:";",endsWithParent:!0,contains:[{className:"attribute",begin:/\S/,end:":",excludeEnd:!0,starts:{endsWithParent:!0,excludeEnd:!0,contains:[{begin:/[\w-]+\(/,returnBegin:!0,contains:[{className:"built_in",begin:/[\w-]+/},{begin:/\(/,end:/\)/,contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE]}]},e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{className:"number",begin:"#[0-9A-Fa-f]+"},{className:"meta",begin:"!important"}]}}]};return{name:"CSS",case_insensitive:!0,illegal:/[=\/|'\$]/,contains:[e.C_BLOCK_COMMENT_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/},{className:"selector-class",begin:/\.[A-Za-z0-9_-]+/},{className:"selector-attr",begin:/\[/,end:/\]/,illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},{className:"selector-pseudo",begin:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{begin:"@(page|font-face)",lexemes:"@[a-z-]+",keywords:"@page @font-face"},{begin:"@",end:"[{;]",illegal:/:/,returnBegin:!0,contains:[{className:"keyword",begin:/@\-?\w[\w]*(\-\w+)*/},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:"and or not only",contains:[{begin:/[a-z-]+:/,className:"attribute"},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.CSS_NUMBER_MODE]}]},{className:"selector-tag",begin:"[a-zA-Z-][a-zA-Z0-9_-]*",relevance:0},{begin:"{",end:"}",illegal:/\S/,contains:[e.C_BLOCK_COMMENT_MODE,n]}]}}}()); -hljs.registerLanguage("diff",function(){"use strict";return function(e){return{name:"Diff",aliases:["patch"],contains:[{className:"meta",relevance:10,variants:[{begin:/^@@ +\-\d+,\d+ +\+\d+,\d+ +@@$/},{begin:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{begin:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{className:"comment",variants:[{begin:/Index: /,end:/$/},{begin:/={3,}/,end:/$/},{begin:/^\-{3}/,end:/$/},{begin:/^\*{3} /,end:/$/},{begin:/^\+{3}/,end:/$/},{begin:/^\*{15}$/}]},{className:"addition",begin:"^\\+",end:"$"},{className:"deletion",begin:"^\\-",end:"$"},{className:"addition",begin:"^\\!",end:"$"}]}}}()); -hljs.registerLanguage("go",function(){"use strict";return function(e){var n={keyword:"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune",literal:"true false iota nil",built_in:"append cap close complex copy imag len make new panic print println real recover delete"};return{name:"Go",aliases:["golang"],keywords:n,illegal:"e(n)).join("")}return function(a){var s={className:"number",relevance:0,variants:[{begin:/([\+\-]+)?[\d]+_[\d_]+/},{begin:a.NUMBER_RE}]},i=a.COMMENT();i.variants=[{begin:/;/,end:/$/},{begin:/#/,end:/$/}];var t={className:"variable",variants:[{begin:/\$[\w\d"][\w\d_]*/},{begin:/\$\{(.*?)}/}]},r={className:"literal",begin:/\bon|off|true|false|yes|no\b/},l={className:"string",contains:[a.BACKSLASH_ESCAPE],variants:[{begin:"'''",end:"'''",relevance:10},{begin:'"""',end:'"""',relevance:10},{begin:'"',end:'"'},{begin:"'",end:"'"}]},c={begin:/\[/,end:/\]/,contains:[i,r,t,l,s,"self"],relevance:0},g="("+[/[A-Za-z0-9_-]+/,/"(\\"|[^"])*"/,/'[^']*'/].map(n=>e(n)).join("|")+")";return{name:"TOML, also INI",aliases:["toml"],case_insensitive:!0,illegal:/\S/,contains:[i,{className:"section",begin:/\[+/,end:/\]+/},{begin:n(g,"(\\s*\\.\\s*",g,")*",n("(?=",/\s*=\s*[^#\s]/,")")),className:"attr",starts:{end:/$/,contains:[i,c,r,t,l,s]}}]}}}()); -hljs.registerLanguage("java",function(){"use strict";function e(e){return e?"string"==typeof e?e:e.source:null}function n(e){return a("(",e,")?")}function a(...n){return n.map(n=>e(n)).join("")}function s(...n){return"("+n.map(n=>e(n)).join("|")+")"}return function(e){var t="false synchronized int abstract float private char boolean var static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",i={className:"meta",begin:"@[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*",contains:[{begin:/\(/,end:/\)/,contains:["self"]}]},r=e=>a("[",e,"]+([",e,"_]*[",e,"]+)?"),c={className:"number",variants:[{begin:`\\b(0[bB]${r("01")})[lL]?`},{begin:`\\b(0${r("0-7")})[dDfFlL]?`},{begin:a(/\b0[xX]/,s(a(r("a-fA-F0-9"),/\./,r("a-fA-F0-9")),a(r("a-fA-F0-9"),/\.?/),a(/\./,r("a-fA-F0-9"))),/([pP][+-]?(\d+))?/,/[fFdDlL]?/)},{begin:a(/\b/,s(a(/\d*\./,r("\\d")),r("\\d")),/[eE][+-]?[\d]+[dDfF]?/)},{begin:a(/\b/,r(/\d/),n(/\.?/),n(r(/\d/)),/[dDfFlL]?/)}],relevance:0};return{name:"Java",aliases:["jsp"],keywords:t,illegal:/<\/|#/,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"class",beginKeywords:"class interface",end:/[{;=]/,excludeEnd:!0,keywords:"class interface",illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"new throw return else",relevance:0},{className:"function",begin:"([À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(<[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*(\\s*,\\s*[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*)*>)?\\s+)+"+e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:t,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"params",begin:/\(/,end:/\)/,keywords:t,relevance:0,contains:[i,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},c,i]}}}()); -hljs.registerLanguage("javascript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);function s(e){return r("(?=",e,")")}function r(...e){return e.map(e=>(function(e){return e?"string"==typeof e?e:e.source:null})(e)).join("")}return function(t){var i="[A-Za-z$_][0-9A-Za-z$_]*",c={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/},o={$pattern:"[A-Za-z$_][0-9A-Za-z$_]*",keyword:e.join(" "),literal:n.join(" "),built_in:a.join(" ")},l={className:"number",variants:[{begin:"\\b(0[bB][01]+)n?"},{begin:"\\b(0[oO][0-7]+)n?"},{begin:t.C_NUMBER_RE+"n?"}],relevance:0},E={className:"subst",begin:"\\$\\{",end:"\\}",keywords:o,contains:[]},d={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"xml"}},g={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[t.BACKSLASH_ESCAPE,E],subLanguage:"css"}},u={className:"string",begin:"`",end:"`",contains:[t.BACKSLASH_ESCAPE,E]};E.contains=[t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,d,g,u,l,t.REGEXP_MODE];var b=E.contains.concat([{begin:/\(/,end:/\)/,contains:["self"].concat(E.contains,[t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE])},t.C_BLOCK_COMMENT_MODE,t.C_LINE_COMMENT_MODE]),_={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:b};return{name:"JavaScript",aliases:["js","jsx","mjs","cjs"],keywords:o,contains:[t.SHEBANG({binary:"node",relevance:5}),{className:"meta",relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},t.APOS_STRING_MODE,t.QUOTE_STRING_MODE,d,g,u,t.C_LINE_COMMENT_MODE,t.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+",contains:[{className:"type",begin:"\\{",end:"\\}",relevance:0},{className:"variable",begin:i+"(?=\\s*(-)|$)",endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]}),t.C_BLOCK_COMMENT_MODE,l,{begin:r(/[{,\n]\s*/,s(r(/(((\/\/.*)|(\/\*(.|\n)*\*\/))\s*)*/,i+"\\s*:"))),relevance:0,contains:[{className:"attr",begin:i+s("\\s*:"),relevance:0}]},{begin:"("+t.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[t.C_LINE_COMMENT_MODE,t.C_BLOCK_COMMENT_MODE,t.REGEXP_MODE,{className:"function",begin:"(\\([^(]*(\\([^(]*(\\([^(]*\\))?\\))?\\)|"+t.UNDERSCORE_IDENT_RE+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:t.UNDERSCORE_IDENT_RE},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:o,contains:b}]}]},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{variants:[{begin:"<>",end:""},{begin:c.begin,end:c.end}],subLanguage:"xml",contains:[{begin:c.begin,end:c.end,skip:!0,contains:["self"]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/\{/,excludeEnd:!0,contains:[t.inherit(t.TITLE_MODE,{begin:i}),_],illegal:/\[|%/},{begin:/\$[(.]/},t.METHOD_GUARD,{className:"class",beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"\[\]]/,contains:[{beginKeywords:"extends"},t.UNDERSCORE_TITLE_MODE]},{beginKeywords:"constructor",end:/\{/,excludeEnd:!0},{begin:"(get|set)\\s+(?="+i+"\\()",end:/{/,keywords:"get set",contains:[t.inherit(t.TITLE_MODE,{begin:i}),{begin:/\(\)/},_]}],illegal:/#(?!!)/}}}()); -hljs.registerLanguage("json",function(){"use strict";return function(n){var e={literal:"true false null"},i=[n.C_LINE_COMMENT_MODE,n.C_BLOCK_COMMENT_MODE],t=[n.QUOTE_STRING_MODE,n.C_NUMBER_MODE],a={end:",",endsWithParent:!0,excludeEnd:!0,contains:t,keywords:e},l={begin:"{",end:"}",contains:[{className:"attr",begin:/"/,end:/"/,contains:[n.BACKSLASH_ESCAPE],illegal:"\\n"},n.inherit(a,{begin:/:/})].concat(i),illegal:"\\S"},s={begin:"\\[",end:"\\]",contains:[n.inherit(a)],illegal:"\\S"};return t.push(l,s),i.forEach((function(n){t.push(n)})),{name:"JSON",contains:t,keywords:e,illegal:"\\S"}}}()); -hljs.registerLanguage("kotlin",function(){"use strict";return function(e){var n={keyword:"abstract as val var vararg get set class object open private protected public noinline crossinline dynamic final enum if else do while for when throw try catch finally import package is in fun override companion reified inline lateinit init interface annotation data sealed internal infix operator out by constructor super tailrec where const inner suspend typealias external expect actual trait volatile transient native default",built_in:"Byte Short Char Int Long Boolean Float Double Void Unit Nothing",literal:"true false null"},a={className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"@"},i={className:"subst",begin:"\\${",end:"}",contains:[e.C_NUMBER_MODE]},s={className:"variable",begin:"\\$"+e.UNDERSCORE_IDENT_RE},t={className:"string",variants:[{begin:'"""',end:'"""(?=[^"])',contains:[s,i]},{begin:"'",end:"'",illegal:/\n/,contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"',illegal:/\n/,contains:[e.BACKSLASH_ESCAPE,s,i]}]};i.contains.push(t);var r={className:"meta",begin:"@(?:file|property|field|get|set|receiver|param|setparam|delegate)\\s*:(?:\\s*"+e.UNDERSCORE_IDENT_RE+")?"},l={className:"meta",begin:"@"+e.UNDERSCORE_IDENT_RE,contains:[{begin:/\(/,end:/\)/,contains:[e.inherit(t,{className:"meta-string"})]}]},c=e.COMMENT("/\\*","\\*/",{contains:[e.C_BLOCK_COMMENT_MODE]}),o={variants:[{className:"type",begin:e.UNDERSCORE_IDENT_RE},{begin:/\(/,end:/\)/,contains:[]}]},d=o;return d.variants[1].contains=[o],o.variants[1].contains=[d],{name:"Kotlin",aliases:["kt"],keywords:n,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.C_LINE_COMMENT_MODE,c,{className:"keyword",begin:/\b(break|continue|return|this)\b/,starts:{contains:[{className:"symbol",begin:/@\w+/}]}},a,r,l,{className:"function",beginKeywords:"fun",end:"[(]|$",returnBegin:!0,excludeEnd:!0,keywords:n,illegal:/fun\s+(<.*>)?[^\s\(]+(\s+[^\s\(]+)\s*=/,relevance:5,contains:[{begin:e.UNDERSCORE_IDENT_RE+"\\s*\\(",returnBegin:!0,relevance:0,contains:[e.UNDERSCORE_TITLE_MODE]},{className:"type",begin://,keywords:"reified",relevance:0},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:n,relevance:0,contains:[{begin:/:/,end:/[=,\/]/,endsWithParent:!0,contains:[o,e.C_LINE_COMMENT_MODE,c],relevance:0},e.C_LINE_COMMENT_MODE,c,r,l,t,e.C_NUMBER_MODE]},c]},{className:"class",beginKeywords:"class interface trait",end:/[:\{(]|$/,excludeEnd:!0,illegal:"extends implements",contains:[{beginKeywords:"public protected internal private constructor"},e.UNDERSCORE_TITLE_MODE,{className:"type",begin://,excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:/[,:]\s*/,end:/[<\(,]|$/,excludeBegin:!0,returnEnd:!0},r,l]},t,{className:"meta",begin:"^#!/usr/bin/env",end:"$",illegal:"\n"},{className:"number",begin:"\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",relevance:0}]}}}()); -hljs.registerLanguage("less",function(){"use strict";return function(e){var n="([\\w-]+|@{[\\w-]+})",a=[],s=[],t=function(e){return{className:"string",begin:"~?"+e+".*?"+e}},r=function(e,n,a){return{className:e,begin:n,relevance:a}},i={begin:"\\(",end:"\\)",contains:s,relevance:0};s.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,t("'"),t('"'),e.CSS_NUMBER_MODE,{begin:"(url|data-uri)\\(",starts:{className:"string",end:"[\\)\\n]",excludeEnd:!0}},r("number","#[0-9A-Fa-f]+\\b"),i,r("variable","@@?[\\w-]+",10),r("variable","@{[\\w-]+}"),r("built_in","~?`[^`]*?`"),{className:"attribute",begin:"[\\w-]+\\s*:",end:":",returnBegin:!0,excludeEnd:!0},{className:"meta",begin:"!important"});var c=s.concat({begin:"{",end:"}",contains:a}),l={beginKeywords:"when",endsWithParent:!0,contains:[{beginKeywords:"and not"}].concat(s)},o={begin:n+"\\s*:",returnBegin:!0,end:"[;}]",relevance:0,contains:[{className:"attribute",begin:n,end:":",excludeEnd:!0,starts:{endsWithParent:!0,illegal:"[<=$]",relevance:0,contains:s}}]},g={className:"keyword",begin:"@(import|media|charset|font-face|(-[a-z]+-)?keyframes|supports|document|namespace|page|viewport|host)\\b",starts:{end:"[;{}]",returnEnd:!0,contains:s,relevance:0}},d={className:"variable",variants:[{begin:"@[\\w-]+\\s*:",relevance:15},{begin:"@[\\w-]+"}],starts:{end:"[;}]",returnEnd:!0,contains:c}},b={variants:[{begin:"[\\.#:&\\[>]",end:"[;{}]"},{begin:n,end:"{"}],returnBegin:!0,returnEnd:!0,illegal:"[<='$\"]",relevance:0,contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,l,r("keyword","all\\b"),r("variable","@{[\\w-]+}"),r("selector-tag",n+"%?",0),r("selector-id","#"+n),r("selector-class","\\."+n,0),r("selector-tag","&",0),{className:"selector-attr",begin:"\\[",end:"\\]"},{className:"selector-pseudo",begin:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{begin:"\\(",end:"\\)",contains:c},{begin:"!important"}]};return a.push(e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,g,d,o,b),{name:"Less",case_insensitive:!0,illegal:"[=>'/<($\"]",contains:a}}}()); -hljs.registerLanguage("lua",function(){"use strict";return function(e){var t={begin:"\\[=*\\[",end:"\\]=*\\]",contains:["self"]},a=[e.COMMENT("--(?!\\[=*\\[)","$"),e.COMMENT("--\\[=*\\[","\\]=*\\]",{contains:[t],relevance:10})];return{name:"Lua",keywords:{$pattern:e.UNDERSCORE_IDENT_RE,literal:"true false nil",keyword:"and break do else elseif end for goto if in local not or repeat return then until while",built_in:"_G _ENV _VERSION __index __newindex __mode __call __metatable __tostring __len __gc __add __sub __mul __div __mod __pow __concat __unm __eq __lt __le assert collectgarbage dofile error getfenv getmetatable ipairs load loadfile loadstring module next pairs pcall print rawequal rawget rawset require select setfenv setmetatable tonumber tostring type unpack xpcall arg self coroutine resume yield status wrap create running debug getupvalue debug sethook getmetatable gethook setmetatable setlocal traceback setfenv getinfo setupvalue getlocal getregistry getfenv io lines write close flush open output type read stderr stdin input stdout popen tmpfile math log max acos huge ldexp pi cos tanh pow deg tan cosh sinh random randomseed frexp ceil floor rad abs sqrt modf asin min mod fmod log10 atan2 exp sin atan os exit setlocale date getenv difftime remove time clock tmpname rename execute package preload loadlib loaded loaders cpath config path seeall string sub upper len gfind rep find match char dump gmatch reverse byte format gsub lower table setn insert getn foreachi maxn foreach concat sort remove"},contains:a.concat([{className:"function",beginKeywords:"function",end:"\\)",contains:[e.inherit(e.TITLE_MODE,{begin:"([_a-zA-Z]\\w*\\.)*([_a-zA-Z]\\w*:)?[_a-zA-Z]\\w*"}),{className:"params",begin:"\\(",endsWithParent:!0,contains:a}].concat(a)},e.C_NUMBER_MODE,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{className:"string",begin:"\\[=*\\[",end:"\\]=*\\]",contains:[t],relevance:5}])}}}()); -hljs.registerLanguage("makefile",function(){"use strict";return function(e){var i={className:"variable",variants:[{begin:"\\$\\("+e.UNDERSCORE_IDENT_RE+"\\)",contains:[e.BACKSLASH_ESCAPE]},{begin:/\$[@%`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin:"",relevance:10,contains:[a,i,t,s,{begin:"\\[",end:"\\]",contains:[{className:"meta",begin:"",contains:[a,s,i,t]}]}]},e.COMMENT("\x3c!--","--\x3e",{relevance:10}),{begin:"<\\!\\[CDATA\\[",end:"\\]\\]>",relevance:10},n,{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:")",end:">",keywords:{name:"style"},contains:[c],starts:{end:"",returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:")",end:">",keywords:{name:"script"},contains:[c],starts:{end:"<\/script>",returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:"",contains:[{className:"name",begin:/[^\/><\s]+/,relevance:0},c]}]}}}()); -hljs.registerLanguage("markdown",function(){"use strict";return function(n){const e={begin:"<",end:">",subLanguage:"xml",relevance:0},a={begin:"\\[.+?\\][\\(\\[].*?[\\)\\]]",returnBegin:!0,contains:[{className:"string",begin:"\\[",end:"\\]",excludeBegin:!0,returnEnd:!0,relevance:0},{className:"link",begin:"\\]\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0},{className:"symbol",begin:"\\]\\[",end:"\\]",excludeBegin:!0,excludeEnd:!0}],relevance:10},i={className:"strong",contains:[],variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\*{2}/,end:/\*{2}/}]},s={className:"emphasis",contains:[],variants:[{begin:/\*(?!\*)/,end:/\*/},{begin:/_(?!_)/,end:/_/,relevance:0}]};i.contains.push(s),s.contains.push(i);var c=[e,a];return i.contains=i.contains.concat(c),s.contains=s.contains.concat(c),{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[{className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:c=c.concat(i,s)},{begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",contains:c}]}]},e,{className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",end:"\\s+",excludeEnd:!0},i,s,{className:"quote",begin:"^>\\s+",contains:c,end:"$"},{className:"code",variants:[{begin:"(`{3,})(.|\\n)*?\\1`*[ ]*"},{begin:"(~{3,})(.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},{begin:"^[-\\*]{3,}",end:"$"},a,{begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]}]}}}()); -hljs.registerLanguage("nginx",function(){"use strict";return function(e){var n={className:"variable",variants:[{begin:/\$\d+/},{begin:/\$\{/,end:/}/},{begin:"[\\$\\@]"+e.UNDERSCORE_IDENT_RE}]},a={endsWithParent:!0,keywords:{$pattern:"[a-z/_]+",literal:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},relevance:0,illegal:"=>",contains:[e.HASH_COMMENT_MODE,{className:"string",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:/"/,end:/"/},{begin:/'/,end:/'/}]},{begin:"([a-z]+):/",end:"\\s",endsWithParent:!0,excludeEnd:!0,contains:[n]},{className:"regexp",contains:[e.BACKSLASH_ESCAPE,n],variants:[{begin:"\\s\\^",end:"\\s|{|;",returnEnd:!0},{begin:"~\\*?\\s+",end:"\\s|{|;",returnEnd:!0},{begin:"\\*(\\.[a-z\\-]+)+"},{begin:"([a-z\\-]+\\.)+\\*"}]},{className:"number",begin:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{className:"number",begin:"\\b\\d+[kKmMgGdshdwy]*\\b",relevance:0},n]};return{name:"Nginx config",aliases:["nginxconf"],contains:[e.HASH_COMMENT_MODE,{begin:e.UNDERSCORE_IDENT_RE+"\\s+{",returnBegin:!0,end:"{",contains:[{className:"section",begin:e.UNDERSCORE_IDENT_RE}],relevance:0},{begin:e.UNDERSCORE_IDENT_RE+"\\s",end:";|{",returnBegin:!0,contains:[{className:"attribute",begin:e.UNDERSCORE_IDENT_RE,starts:a}],relevance:0}],illegal:"[^\\s\\}]"}}}()); -hljs.registerLanguage("objectivec",function(){"use strict";return function(e){var n=/[a-zA-Z@][a-zA-Z0-9_]*/,_={$pattern:n,keyword:"@interface @class @protocol @implementation"};return{name:"Objective-C",aliases:["mm","objc","obj-c"],keywords:{$pattern:n,keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},illegal:"/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class",begin:"("+_.keyword.split(" ").join("|")+")\\b",end:"({|$)",excludeEnd:!0,keywords:_,contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE,relevance:0}]}}}()); -hljs.registerLanguage("perl",function(){"use strict";return function(e){var n={$pattern:/[\w.]+/,keyword:"getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qq fileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmget sub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedir ioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when"},t={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:n},s={begin:"->{",end:"}"},r={variants:[{begin:/\$\d/},{begin:/[\$%@](\^\w\b|#\w+(::\w+)*|{\w+}|\w+(::\w*)*)/},{begin:/[\$%@][^\s\w{]/,relevance:0}]},i=[e.BACKSLASH_ESCAPE,t,r],a=[r,e.HASH_COMMENT_MODE,e.COMMENT("^\\=\\w","\\=cut",{endsWithParent:!0}),s,{className:"string",contains:i,variants:[{begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[",end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*\\<",end:"\\>",relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'",contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE]},{begin:"{\\w+}",contains:[],relevance:0},{begin:"-?\\w+\\s*\\=\\>",contains:[],relevance:0}]},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*",keywords:"split return print reverse grep",relevance:0,contains:[e.HASH_COMMENT_MODE,{className:"regexp",begin:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",relevance:10},{className:"regexp",begin:"(m|qr)?/",end:"/[a-z]*",contains:[e.BACKSLASH_ESCAPE],relevance:0}]},{className:"function",beginKeywords:"sub",end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}]}];return t.contains=a,s.contains=a,{name:"Perl",aliases:["pl","pm"],keywords:n,contains:a}}}()); -hljs.registerLanguage("php",function(){"use strict";return function(e){var r={begin:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},t={className:"meta",variants:[{begin:/<\?php/,relevance:10},{begin:/<\?[=]?/},{begin:/\?>/}]},a={className:"string",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:'b"',end:'"'},{begin:"b'",end:"'"},e.inherit(e.APOS_STRING_MODE,{illegal:null}),e.inherit(e.QUOTE_STRING_MODE,{illegal:null})]},n={variants:[e.BINARY_NUMBER_MODE,e.C_NUMBER_MODE]},i={keyword:"__CLASS__ __DIR__ __FILE__ __FUNCTION__ __LINE__ __METHOD__ __NAMESPACE__ __TRAIT__ die echo exit include include_once print require require_once array abstract and as binary bool boolean break callable case catch class clone const continue declare default do double else elseif empty enddeclare endfor endforeach endif endswitch endwhile eval extends final finally float for foreach from global goto if implements instanceof insteadof int integer interface isset iterable list new object or private protected public real return string switch throw trait try unset use var void while xor yield",literal:"false null true",built_in:"Error|0 AppendIterator ArgumentCountError ArithmeticError ArrayIterator ArrayObject AssertionError BadFunctionCallException BadMethodCallException CachingIterator CallbackFilterIterator CompileError Countable DirectoryIterator DivisionByZeroError DomainException EmptyIterator ErrorException Exception FilesystemIterator FilterIterator GlobIterator InfiniteIterator InvalidArgumentException IteratorIterator LengthException LimitIterator LogicException MultipleIterator NoRewindIterator OutOfBoundsException OutOfRangeException OuterIterator OverflowException ParentIterator ParseError RangeException RecursiveArrayIterator RecursiveCachingIterator RecursiveCallbackFilterIterator RecursiveDirectoryIterator RecursiveFilterIterator RecursiveIterator RecursiveIteratorIterator RecursiveRegexIterator RecursiveTreeIterator RegexIterator RuntimeException SeekableIterator SplDoublyLinkedList SplFileInfo SplFileObject SplFixedArray SplHeap SplMaxHeap SplMinHeap SplObjectStorage SplObserver SplObserver SplPriorityQueue SplQueue SplStack SplSubject SplSubject SplTempFileObject TypeError UnderflowException UnexpectedValueException ArrayAccess Closure Generator Iterator IteratorAggregate Serializable Throwable Traversable WeakReference Directory __PHP_Incomplete_Class parent php_user_filter self static stdClass"};return{aliases:["php","php3","php4","php5","php6","php7"],case_insensitive:!0,keywords:i,contains:[e.HASH_COMMENT_MODE,e.COMMENT("//","$",{contains:[t]}),e.COMMENT("/\\*","\\*/",{contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.COMMENT("__halt_compiler.+?;",!1,{endsWithParent:!0,keywords:"__halt_compiler"}),{className:"string",begin:/<<<['"]?\w+['"]?$/,end:/^\w+;?$/,contains:[e.BACKSLASH_ESCAPE,{className:"subst",variants:[{begin:/\$\w+/},{begin:/\{\$/,end:/\}/}]}]},t,{className:"keyword",begin:/\$this\b/},r,{begin:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{className:"function",beginKeywords:"fn function",end:/[;{]/,excludeEnd:!0,illegal:"[$%\\[]",contains:[e.UNDERSCORE_TITLE_MODE,{className:"params",begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:i,contains:["self",r,e.C_BLOCK_COMMENT_MODE,a,n]}]},{className:"class",beginKeywords:"class interface",end:"{",excludeEnd:!0,illegal:/[:\(\$"]/,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"namespace",end:";",illegal:/[\.']/,contains:[e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"use",end:";",contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"=>"},a,n]}}}()); -hljs.registerLanguage("php-template",function(){"use strict";return function(n){return{name:"PHP template",subLanguage:"xml",contains:[{begin:/<\?(php|=)?/,end:/\?>/,subLanguage:"php",contains:[{begin:"/\\*",end:"\\*/",skip:!0},{begin:'b"',end:'"',skip:!0},{begin:"b'",end:"'",skip:!0},n.inherit(n.APOS_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0}),n.inherit(n.QUOTE_STRING_MODE,{illegal:null,className:null,contains:null,skip:!0})]}]}}}()); -hljs.registerLanguage("plaintext",function(){"use strict";return function(t){return{name:"Plain text",aliases:["text","txt"],disableAutodetect:!0}}}()); -hljs.registerLanguage("properties",function(){"use strict";return function(e){var n="[ \\t\\f]*",t="("+n+"[:=]"+n+"|[ \\t\\f]+)",a="([^\\\\:= \\t\\f\\n]|\\\\.)+",s={end:t,relevance:0,starts:{className:"string",end:/$/,relevance:0,contains:[{begin:"\\\\\\n"}]}};return{name:".properties",case_insensitive:!0,illegal:/\S/,contains:[e.COMMENT("^\\s*[!#]","$"),{begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+"+t,returnBegin:!0,contains:[{className:"attr",begin:"([^\\\\\\W:= \\t\\f\\n]|\\\\.)+",endsParent:!0,relevance:0}],starts:s},{begin:a+t,returnBegin:!0,relevance:0,contains:[{className:"meta",begin:a,endsParent:!0,relevance:0}],starts:s},{className:"attr",relevance:0,begin:a+n+"$"}]}}}()); -hljs.registerLanguage("python",function(){"use strict";return function(e){var n={keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda async await nonlocal|10",built_in:"Ellipsis NotImplemented",literal:"False None True"},a={className:"meta",begin:/^(>>>|\.\.\.) /},i={className:"subst",begin:/\{/,end:/\}/,keywords:n,illegal:/#/},s={begin:/\{\{/,relevance:0},r={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:/(u|b)?r?'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(u|b)?r?"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a],relevance:10},{begin:/(fr|rf|f)'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(fr|rf|f)"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,a,s,i]},{begin:/(u|r|ur)'/,end:/'/,relevance:10},{begin:/(u|r|ur)"/,end:/"/,relevance:10},{begin:/(b|br)'/,end:/'/},{begin:/(b|br)"/,end:/"/},{begin:/(fr|rf|f)'/,end:/'/,contains:[e.BACKSLASH_ESCAPE,s,i]},{begin:/(fr|rf|f)"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,s,i]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},l={className:"number",relevance:0,variants:[{begin:e.BINARY_NUMBER_RE+"[lLjJ]?"},{begin:"\\b(0o[0-7]+)[lLjJ]?"},{begin:e.C_NUMBER_RE+"[lLjJ]?"}]},t={className:"params",variants:[{begin:/\(\s*\)/,skip:!0,className:null},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,contains:["self",a,l,r,e.HASH_COMMENT_MODE]}]};return i.contains=[r,l,a],{name:"Python",aliases:["py","gyp","ipython"],keywords:n,illegal:/(<\/|->|\?)|=>/,contains:[a,l,{beginKeywords:"if",relevance:0},r,e.HASH_COMMENT_MODE,{variants:[{className:"function",beginKeywords:"def"},{className:"class",beginKeywords:"class"}],end:/:/,illegal:/[${=;\n,]/,contains:[e.UNDERSCORE_TITLE_MODE,t,{begin:/->/,endsWithParent:!0,keywords:"None"}]},{className:"meta",begin:/^[\t ]*@/,end:/$/},{begin:/\b(print|exec)\(/}]}}}()); -hljs.registerLanguage("python-repl",function(){"use strict";return function(n){return{aliases:["pycon"],contains:[{className:"meta",starts:{end:/ |$/,starts:{end:"$",subLanguage:"python"}},variants:[{begin:/^>>>(?=[ ]|$)/},{begin:/^\.\.\.(?=[ ]|$)/}]}]}}}()); -hljs.registerLanguage("ruby",function(){"use strict";return function(e){var n="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",a={keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",literal:"true false nil"},s={className:"doctag",begin:"@[A-Za-z]+"},i={begin:"#<",end:">"},r=[e.COMMENT("#","$",{contains:[s]}),e.COMMENT("^\\=begin","^\\=end",{contains:[s],relevance:10}),e.COMMENT("^__END__","\\n$")],c={className:"subst",begin:"#\\{",end:"}",keywords:a},t={className:"string",contains:[e.BACKSLASH_ESCAPE,c],variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:"%[qQwWx]?\\(",end:"\\)"},{begin:"%[qQwWx]?\\[",end:"\\]"},{begin:"%[qQwWx]?{",end:"}"},{begin:"%[qQwWx]?<",end:">"},{begin:"%[qQwWx]?/",end:"/"},{begin:"%[qQwWx]?%",end:"%"},{begin:"%[qQwWx]?-",end:"-"},{begin:"%[qQwWx]?\\|",end:"\\|"},{begin:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{begin:/<<[-~]?'?(\w+)(?:.|\n)*?\n\s*\1\b/,returnBegin:!0,contains:[{begin:/<<[-~]?'?/},e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,contains:[e.BACKSLASH_ESCAPE,c]})]}]},b={className:"params",begin:"\\(",end:"\\)",endsParent:!0,keywords:a},d=[t,i,{className:"class",beginKeywords:"class module",end:"$|;",illegal:/=/,contains:[e.inherit(e.TITLE_MODE,{begin:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{begin:"<\\s*",contains:[{begin:"("+e.IDENT_RE+"::)?"+e.IDENT_RE}]}].concat(r)},{className:"function",beginKeywords:"def",end:"$|;",contains:[e.inherit(e.TITLE_MODE,{begin:n}),b].concat(r)},{begin:e.IDENT_RE+"::"},{className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"(\\!|\\?)?:",relevance:0},{className:"symbol",begin:":(?!\\s)",contains:[t,{begin:n}],relevance:0},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{className:"params",begin:/\|/,end:/\|/,keywords:a},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[i,{className:"regexp",contains:[e.BACKSLASH_ESCAPE,c],illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:"%r{",end:"}[a-z]*"},{begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}]}].concat(r),relevance:0}].concat(r);c.contains=d,b.contains=d;var g=[{begin:/^\s*=>/,starts:{end:"$",contains:d}},{className:"meta",begin:"^([>?]>|[\\w#]+\\(\\w+\\):\\d+:\\d+>|(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>)",starts:{end:"$",contains:d}}];return{name:"Ruby",aliases:["rb","gemspec","podspec","thor","irb"],keywords:a,illegal:/\/\*/,contains:r.concat(g).concat(d)}}}()); -hljs.registerLanguage("rust",function(){"use strict";return function(e){var n="([ui](8|16|32|64|128|size)|f(32|64))?",t="drop i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize f32 f64 str char bool Box Option Result String Vec Copy Send Sized Sync Drop Fn FnMut FnOnce ToOwned Clone Debug PartialEq PartialOrd Eq Ord AsRef AsMut Into From Default Iterator Extend IntoIterator DoubleEndedIterator ExactSizeIterator SliceConcatExt ToString assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! panic! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln! macro_rules! assert_ne! debug_assert_ne!";return{name:"Rust",aliases:["rs"],keywords:{$pattern:e.IDENT_RE+"!?",keyword:"abstract as async await become box break const continue crate do dyn else enum extern false final fn for if impl in let loop macro match mod move mut override priv pub ref return self Self static struct super trait true try type typeof unsafe unsized use virtual where while yield",literal:"true false Some None Ok Err",built_in:t},illegal:""}]}}}()); -hljs.registerLanguage("scss",function(){"use strict";return function(e){var t={className:"variable",begin:"(\\$[a-zA-Z-][a-zA-Z0-9_-]*)\\b"},i={className:"number",begin:"#[0-9A-Fa-f]+"};return e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,e.C_BLOCK_COMMENT_MODE,{name:"SCSS",case_insensitive:!0,illegal:"[=/|']",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{className:"selector-id",begin:"\\#[A-Za-z0-9_-]+",relevance:0},{className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0},{className:"selector-attr",begin:"\\[",end:"\\]",illegal:"$"},{className:"selector-tag",begin:"\\b(a|abbr|acronym|address|area|article|aside|audio|b|base|big|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|command|datalist|dd|del|details|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|frame|frameset|(h[1-6])|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|map|mark|meta|meter|nav|noframes|noscript|object|ol|optgroup|option|output|p|param|pre|progress|q|rp|rt|ruby|samp|script|section|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|title|tr|tt|ul|var|video)\\b",relevance:0},{className:"selector-pseudo",begin:":(visited|valid|root|right|required|read-write|read-only|out-range|optional|only-of-type|only-child|nth-of-type|nth-last-of-type|nth-last-child|nth-child|not|link|left|last-of-type|last-child|lang|invalid|indeterminate|in-range|hover|focus|first-of-type|first-line|first-letter|first-child|first|enabled|empty|disabled|default|checked|before|after|active)"},{className:"selector-pseudo",begin:"::(after|before|choices|first-letter|first-line|repeat-index|repeat-item|selection|value)"},t,{className:"attribute",begin:"\\b(src|z-index|word-wrap|word-spacing|word-break|width|widows|white-space|visibility|vertical-align|unicode-bidi|transition-timing-function|transition-property|transition-duration|transition-delay|transition|transform-style|transform-origin|transform|top|text-underline-position|text-transform|text-shadow|text-rendering|text-overflow|text-indent|text-decoration-style|text-decoration-line|text-decoration-color|text-decoration|text-align-last|text-align|tab-size|table-layout|right|resize|quotes|position|pointer-events|perspective-origin|perspective|page-break-inside|page-break-before|page-break-after|padding-top|padding-right|padding-left|padding-bottom|padding|overflow-y|overflow-x|overflow-wrap|overflow|outline-width|outline-style|outline-offset|outline-color|outline|orphans|order|opacity|object-position|object-fit|normal|none|nav-up|nav-right|nav-left|nav-index|nav-down|min-width|min-height|max-width|max-height|mask|marks|margin-top|margin-right|margin-left|margin-bottom|margin|list-style-type|list-style-position|list-style-image|list-style|line-height|letter-spacing|left|justify-content|initial|inherit|ime-mode|image-orientation|image-resolution|image-rendering|icon|hyphens|height|font-weight|font-variant-ligatures|font-variant|font-style|font-stretch|font-size-adjust|font-size|font-language-override|font-kerning|font-feature-settings|font-family|font|float|flex-wrap|flex-shrink|flex-grow|flex-flow|flex-direction|flex-basis|flex|filter|empty-cells|display|direction|cursor|counter-reset|counter-increment|content|column-width|column-span|column-rule-width|column-rule-style|column-rule-color|column-rule|column-gap|column-fill|column-count|columns|color|clip-path|clip|clear|caption-side|break-inside|break-before|break-after|box-sizing|box-shadow|box-decoration-break|bottom|border-width|border-top-width|border-top-style|border-top-right-radius|border-top-left-radius|border-top-color|border-top|border-style|border-spacing|border-right-width|border-right-style|border-right-color|border-right|border-radius|border-left-width|border-left-style|border-left-color|border-left|border-image-width|border-image-source|border-image-slice|border-image-repeat|border-image-outset|border-image|border-color|border-collapse|border-bottom-width|border-bottom-style|border-bottom-right-radius|border-bottom-left-radius|border-bottom-color|border-bottom|border|background-size|background-repeat|background-position|background-origin|background-image|background-color|background-clip|background-attachment|background-blend-mode|background|backface-visibility|auto|animation-timing-function|animation-play-state|animation-name|animation-iteration-count|animation-fill-mode|animation-duration|animation-direction|animation-delay|animation|align-self|align-items|align-content)\\b",illegal:"[^\\s]"},{begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b"},{begin:":",end:";",contains:[t,i,e.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,{className:"meta",begin:"!important"}]},{begin:"@(page|font-face)",lexemes:"@[a-z-]+",keywords:"@page @font-face"},{begin:"@",end:"[{;]",returnBegin:!0,keywords:"and or not only",contains:[{begin:"@[a-z-]+",className:"keyword"},t,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,i,e.CSS_NUMBER_MODE]}]}}}()); -hljs.registerLanguage("shell",function(){"use strict";return function(s){return{name:"Shell Session",aliases:["console"],contains:[{className:"meta",begin:"^\\s{0,3}[/\\w\\d\\[\\]()@-]*[>%$#]",starts:{end:"$",subLanguage:"bash"}}]}}}()); -hljs.registerLanguage("sql",function(){"use strict";return function(e){var t=e.COMMENT("--","$");return{name:"SQL",case_insensitive:!0,illegal:/[<>{}*]/,contains:[{beginKeywords:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke comment values with",end:/;/,endsWithParent:!0,keywords:{$pattern:/[\w\.]+/,keyword:"as abort abs absolute acc acce accep accept access accessed accessible account acos action activate add addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias all allocate allow alter always analyze ancillary and anti any anydata anydataset anyschema anytype apply archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound bucket buffer_cache buffer_pool build bulk by byte byteordermark bytes cache caching call calling cancel capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base char_length character_length characters characterset charindex charset charsetform charsetid check checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation collect colu colum column column_value columns columns_updated comment commit compact compatibility compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection consider consistent constant constraint constraints constructor container content contents context contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime customdatum cycle data database databases datafile datafiles datalength date_add date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor deterministic diagnostics difference dimension direct_load directory disable disable_all disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div do document domain dotnet double downgrade drop dumpfile duplicate duration each edition editionable editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding execu execut execute exempt exists exit exp expire explain explode export export_set extended extent external external_1 external_2 externally extract failed failed_login_attempts failover failure far fast feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final finish first first_value fixed flash_cache flashback floor flush following follows for forall force foreign form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ftp full function general generated get get_format get_lock getdate getutcdate global global_name globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex hierarchy high high_priority hosts hour hours http id ident_current ident_incr ident_seed identified identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile initial initialized initially initrans inmemory inner innodb input insert install instance instantiable instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists keep keep_duplicates key keys kill language large last last_day last_insert_id last_value lateral lax lcase lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call logoff logon logs long loop low low_priority lower lpad lrtrim ltrim main make_set makedate maketime managed management manual map mapping mask master master_pos_wait match matched materialized max maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans md5 measures median medium member memcompress memory merge microsecond mid migration min minextents minimum mining minus minute minutes minvalue missing mod mode model modification modify module monitoring month months mount move movement multiset mutex name name_const names nan national native natural nav nchar nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck noswitch not nothing notice notnull notrim novalidate now nowait nth_value nullif nulls num numb numbe nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary out outer outfile outline output over overflow overriding package pad parallel parallel_enable parameters parent parse partial partition partitions pascal passing password password_grace_time password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction prediction_cost prediction_details prediction_probability prediction_set prepare present preserve prior priority private private_sga privileges procedural procedure procedure_analyze processlist profiles project prompt protection public publishingservername purge quarter query quick quiesce quota quotename radians raise rand range rank raw read reads readsize rebuild record records recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename repair repeat replace replicate replication required reset resetlogs resize resource respect restore restricted result result_cache resumable resume retention return returning returns reuse reverse revoke right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll sdo_georaster sdo_topo_geometry search sec_to_time second seconds section securefile security seed segment select self semi sequence sequential serializable server servererror session session_user sessions_per_user set sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone standby start starting startup statement static statistics stats_binomial_test stats_crosstab stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime table tables tablespace tablesample tan tdo template temporary terminated tertiary_weights test than then thread through tier ties time time_format time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unnest unpivot unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear wellformed when whene whenev wheneve whenever where while whitespace window with within without work wrapped xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek",literal:"true false null unknown",built_in:"array bigint binary bit blob bool boolean char character date dec decimal float int int8 integer interval number numeric real record serial serial8 smallint text time timestamp tinyint varchar varchar2 varying void"},contains:[{className:"string",begin:"'",end:"'",contains:[{begin:"''"}]},{className:"string",begin:'"',end:'"',contains:[{begin:'""'}]},{className:"string",begin:"`",end:"`"},e.C_NUMBER_MODE,e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]},e.C_BLOCK_COMMENT_MODE,t,e.HASH_COMMENT_MODE]}}}()); -hljs.registerLanguage("swift",function(){"use strict";return function(e){var i={keyword:"#available #colorLiteral #column #else #elseif #endif #file #fileLiteral #function #if #imageLiteral #line #selector #sourceLocation _ __COLUMN__ __FILE__ __FUNCTION__ __LINE__ Any as as! as? associatedtype associativity break case catch class continue convenience default defer deinit didSet do dynamic dynamicType else enum extension fallthrough false fileprivate final for func get guard if import in indirect infix init inout internal is lazy left let mutating nil none nonmutating open operator optional override postfix precedence prefix private protocol Protocol public repeat required rethrows return right self Self set static struct subscript super switch throw throws true try try! try? Type typealias unowned var weak where while willSet",literal:"true false nil",built_in:"abs advance alignof alignofValue anyGenerator assert assertionFailure bridgeFromObjectiveC bridgeFromObjectiveCUnconditional bridgeToObjectiveC bridgeToObjectiveCUnconditional c compactMap contains count countElements countLeadingZeros debugPrint debugPrintln distance dropFirst dropLast dump encodeBitsAsWords enumerate equal fatalError filter find getBridgedObjectiveCType getVaList indices insertionSort isBridgedToObjectiveC isBridgedVerbatimToObjectiveC isUniquelyReferenced isUniquelyReferencedNonObjC join lazy lexicographicalCompare map max maxElement min minElement numericCast overlaps partition posix precondition preconditionFailure print println quickSort readLine reduce reflect reinterpretCast reverse roundUpToAlignment sizeof sizeofValue sort split startsWith stride strideof strideofValue swap toString transcode underestimateCount unsafeAddressOf unsafeBitCast unsafeDowncast unsafeUnwrap unsafeReflect withExtendedLifetime withObjectAtPlusZero withUnsafePointer withUnsafePointerToObject withUnsafeMutablePointer withUnsafeMutablePointers withUnsafePointer withUnsafePointers withVaList zip"},n=e.COMMENT("/\\*","\\*/",{contains:["self"]}),t={className:"subst",begin:/\\\(/,end:"\\)",keywords:i,contains:[]},a={className:"string",contains:[e.BACKSLASH_ESCAPE,t],variants:[{begin:/"""/,end:/"""/},{begin:/"/,end:/"/}]},r={className:"number",begin:"\\b([\\d_]+(\\.[\\deE_]+)?|0x[a-fA-F0-9_]+(\\.[a-fA-F0-9p_]+)?|0b[01_]+|0o[0-7_]+)\\b",relevance:0};return t.contains=[r],{name:"Swift",keywords:i,contains:[a,e.C_LINE_COMMENT_MODE,n,{className:"type",begin:"\\b[A-Z][\\wÀ-ʸ']*[!?]"},{className:"type",begin:"\\b[A-Z][\\wÀ-ʸ']*",relevance:0},r,{className:"function",beginKeywords:"func",end:"{",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][0-9A-Za-z$_]*/}),{begin://},{className:"params",begin:/\(/,end:/\)/,endsParent:!0,keywords:i,contains:["self",r,a,e.C_BLOCK_COMMENT_MODE,{begin:":"}],illegal:/["']/}],illegal:/\[|%/},{className:"class",beginKeywords:"struct protocol class extension enum",keywords:i,end:"\\{",excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/})]},{className:"meta",begin:"(@discardableResult|@warn_unused_result|@exported|@lazy|@noescape|@NSCopying|@NSManaged|@objc|@objcMembers|@convention|@required|@noreturn|@IBAction|@IBDesignable|@IBInspectable|@IBOutlet|@infix|@prefix|@postfix|@autoclosure|@testable|@available|@nonobjc|@NSApplicationMain|@UIApplicationMain|@dynamicMemberLookup|@propertyWrapper)\\b"},{beginKeywords:"import",end:/$/,contains:[e.C_LINE_COMMENT_MODE,n]}]}}}()); -hljs.registerLanguage("typescript",function(){"use strict";const e=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],n=["true","false","null","undefined","NaN","Infinity"],a=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);return function(r){var t={$pattern:"[A-Za-z$_][0-9A-Za-z$_]*",keyword:e.concat(["type","namespace","typedef","interface","public","private","protected","implements","declare","abstract","readonly"]).join(" "),literal:n.join(" "),built_in:a.concat(["any","void","number","boolean","string","object","never","enum"]).join(" ")},s={className:"meta",begin:"@[A-Za-z$_][0-9A-Za-z$_]*"},i={className:"number",variants:[{begin:"\\b(0[bB][01]+)n?"},{begin:"\\b(0[oO][0-7]+)n?"},{begin:r.C_NUMBER_RE+"n?"}],relevance:0},o={className:"subst",begin:"\\$\\{",end:"\\}",keywords:t,contains:[]},c={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,o],subLanguage:"xml"}},l={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[r.BACKSLASH_ESCAPE,o],subLanguage:"css"}},E={className:"string",begin:"`",end:"`",contains:[r.BACKSLASH_ESCAPE,o]};o.contains=[r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,c,l,E,i,r.REGEXP_MODE];var d={begin:"\\(",end:/\)/,keywords:t,contains:["self",r.QUOTE_STRING_MODE,r.APOS_STRING_MODE,r.NUMBER_MODE]},u={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,contains:[r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,s,d]};return{name:"TypeScript",aliases:["ts"],keywords:t,contains:[r.SHEBANG(),{className:"meta",begin:/^\s*['"]use strict['"]/},r.APOS_STRING_MODE,r.QUOTE_STRING_MODE,c,l,E,r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,i,{begin:"("+r.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",contains:[r.C_LINE_COMMENT_MODE,r.C_BLOCK_COMMENT_MODE,r.REGEXP_MODE,{className:"function",begin:"(\\([^(]*(\\([^(]*(\\([^(]*\\))?\\))?\\)|"+r.UNDERSCORE_IDENT_RE+")\\s*=>",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:r.UNDERSCORE_IDENT_RE},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:t,contains:d.contains}]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/[\{;]/,excludeEnd:!0,keywords:t,contains:["self",r.inherit(r.TITLE_MODE,{begin:"[A-Za-z$_][0-9A-Za-z$_]*"}),u],illegal:/%/,relevance:0},{beginKeywords:"constructor",end:/[\{;]/,excludeEnd:!0,contains:["self",u]},{begin:/module\./,keywords:{built_in:"module"},relevance:0},{beginKeywords:"module",end:/\{/,excludeEnd:!0},{beginKeywords:"interface",end:/\{/,excludeEnd:!0,keywords:"interface extends"},{begin:/\$[(.]/},{begin:"\\."+r.IDENT_RE,relevance:0},s,d]}}}()); -hljs.registerLanguage("yaml",function(){"use strict";return function(e){var n="true false yes no null",a="[\\w#;/?:@&=+$,.~*\\'()[\\]]+",s={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:"{{",end:"}}"},{begin:"%{",end:"}"}]}]},i=e.inherit(s,{variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),l={end:",",endsWithParent:!0,excludeEnd:!0,contains:[],keywords:n,relevance:0},t={begin:"{",end:"}",contains:[l],illegal:"\\n",relevance:0},g={begin:"\\[",end:"\\]",contains:[l],illegal:"\\n",relevance:0},b=[{className:"attr",variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---s*$",relevance:10},{className:"string",begin:"[\\|>]([0-9]?[+-])?[ ]*\\n( *)[\\S ]+\\n(\\2[\\S ]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!\\w+!"+a},{className:"type",begin:"!<"+a+">"},{className:"type",begin:"!"+a},{className:"type",begin:"!!"+a},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"\\-(?=[ ]|$)",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:n,keywords:{literal:n}},{className:"number",begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"},{className:"number",begin:e.C_NUMBER_RE+"\\b"},t,g,s],c=[...b];return c.pop(),c.push(i),l.contains=c,{name:"YAML",case_insensitive:!0,aliases:["yml","YAML"],contains:b}}}()); \ No newline at end of file diff --git a/docs/update/config.md b/docs/update/config.md index 1318f367..c02eabbd 100644 --- a/docs/update/config.md +++ b/docs/update/config.md @@ -75,4 +75,4 @@ $config['remote_terminal'] = [ 'port' => 20002, 'token' => '' ]; -``` \ No newline at end of file +``` diff --git a/docs/update/v1.md b/docs/update/v1.md index 89d53221..cdbd04a3 100644 --- a/docs/update/v1.md +++ b/docs/update/v1.md @@ -217,4 +217,4 @@ $config['server_event_handler_class'] = [ > 更新时间:2020.3.19 -正式版发布。 \ No newline at end of file +正式版发布。 diff --git a/package.json b/package.json new file mode 100644 index 00000000..6785a759 --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "dependencies": { + "vuepress-theme-antdocs": "^1.3.5", + "vuepress": "^1.9.7" + }, + "scripts": { + "docs:dev": "vuepress dev docs", + "docs:build": "vuepress build docs" + } +}