diff --git a/plugin/global/core/plugin.js b/plugin/global/core/plugin.js
index b84e1b94..46163c96 100644
--- a/plugin/global/core/plugin.js
+++ b/plugin/global/core/plugin.js
@@ -29,7 +29,7 @@ class IPlugin {
/** 一级插件 */
class BasePlugin extends IPlugin {
- call(type, meta) {}
+ call(action, meta) {}
}
/** 二级插件 */
@@ -77,8 +77,8 @@ const loadPlugin = async (fixedName, setting, isCustom) => {
}
const LoadPlugins = async (settings, isCustom) => {
- const plugins = { enable: {}, disable: {}, stop: {}, error: {}, nosetting: {} };
- await Promise.all(Object.entries(settings).map(async ([fixedName, setting]) => {
+ const plugins = { enable: {}, disable: {}, stop: {}, error: {}, nosetting: {} }
+ const promises = Object.entries(settings).map(async ([fixedName, setting]) => {
if (!setting) {
plugins.nosetting[fixedName] = fixedName;
} else if (!setting.ENABLE && !setting.enable) {
@@ -96,12 +96,13 @@ const LoadPlugins = async (settings, isCustom) => {
plugins.error[fixedName] = error;
}
}
- }))
+ })
+ await Promise.all(promises)
// log
- const LOG_COLOR = { enable: "32", disable: "33", stop: "34", error: "31", nosetting: "35" };
+ const COLORS = { enable: "32", disable: "33", stop: "34", error: "31", nosetting: "35" };
console.group(`${isCustom ? "Custom" : "Base"} Plugin`);
- Object.entries(plugins).forEach(([t, p]) => console.debug(`[ \x1B[${LOG_COLOR[t]}m${t}\x1b[0m ] [ ${Object.keys(p).length} ]:`, p));
+ Object.entries(plugins).forEach(([t, p]) => console.debug(`[ \x1B[${COLORS[t]}m${t}\x1b[0m ] [ ${Object.keys(p).length} ]:`, p));
console.groupEnd();
return plugins;
diff --git a/plugin/global/core/utils/mixin/dialog.js b/plugin/global/core/utils/mixin/dialog.js
index 1fe76f25..b5d13be4 100644
--- a/plugin/global/core/utils/mixin/dialog.js
+++ b/plugin/global/core/utils/mixin/dialog.js
@@ -182,6 +182,9 @@ class dialog {
case "span":
label = "span";
break
+ case "blockquote":
+ label = "blockquote"
+ break
}
const class_ = comp.inline ? "form-inline-group" : "form-block-group";
const label_ = comp.label ? `<${label}>${comp.label}${genInfo(comp)}${label}>` : "";
diff --git a/plugin/preferences.js b/plugin/preferences.js
index 45989c4e..ea53879d 100644
--- a/plugin/preferences.js
+++ b/plugin/preferences.js
@@ -2,39 +2,32 @@ class preferencesPlugin extends BasePlugin {
hotkey = () => [{ hotkey: this.config.HOTKEY, callback: this.call }]
getSettings = async () => {
- const settings = await this.utils.runtime.readBasePluginSetting();
- const customSettings = await this.utils.runtime.readCustomPluginSetting();
- delete settings.global;
- return [settings, customSettings]
+ const base = await this.utils.runtime.readBasePluginSetting()
+ const custom = await this.utils.runtime.readCustomPluginSetting()
+ delete base.global
+ return [base, custom]
}
- togglePlugin = async (enablePlugins, enableCustomPlugins, showModal = false) => {
- const [settings, customSettings] = await this.getSettings()
-
- const pluginState = {}
- const customPluginState = {}
- Object.keys(settings).forEach(fixedName => (pluginState[fixedName] = { ENABLE: enablePlugins.includes(fixedName) }))
- Object.keys(customSettings).forEach(fixedName => (customPluginState[fixedName] = { enable: enableCustomPlugins.includes(fixedName) }))
-
- // check need update file
- const settingsHasUpdate = Object.entries(settings).some(([name, plugin]) => plugin.ENABLE !== pluginState[name].ENABLE)
- const customSettingsHasUpdate = Object.entries(customSettings).some(([name, plugin]) => plugin.enable !== customPluginState[name].enable)
- if (!settingsHasUpdate && !customSettingsHasUpdate) return
-
- const files = [
- { file: "settings.user.toml", mergeObj: pluginState },
- { file: "custom_plugin.user.toml", mergeObj: customPluginState },
- ]
- for (const { file, mergeObj } of files) {
- const settingPath = await this.utils.runtime.getActualSettingPath(file)
- const settingObj = await this.utils.readTomlFile(settingPath)
- const setting = this.utils.merge(settingObj, mergeObj)
- const newContent = this.utils.stringifyToml(setting)
- const ok = await this.utils.writeFile(settingPath, newContent)
- if (!ok) return
+ togglePlugin = async (enableBasePlugins, enableCustomPlugins) => {
+ const updateSetting = async (file, setting, enablePlugins, enableKey) => {
+ const newState = Object.keys(setting).reduce((acc, fixedName) => {
+ acc[fixedName] = { [enableKey]: enablePlugins.includes(fixedName) }
+ return acc
+ }, {})
+ const needUpdate = Object.entries(setting).some(([name, plugin]) => plugin[enableKey] !== newState[name][enableKey])
+ if (needUpdate) {
+ const settingPath = await this.utils.runtime.getActualSettingPath(file)
+ const settingObj = await this.utils.readTomlFile(settingPath)
+ const mergedSetting = this.utils.merge(settingObj, newState)
+ const newContent = this.utils.stringifyToml(mergedSetting)
+ return this.utils.writeFile(settingPath, newContent)
+ }
}
- if (showModal) {
+ const [base, custom] = await this.getSettings()
+ const baseUpdated = await updateSetting("settings.user.toml", base, enableBasePlugins, "ENABLE")
+ const customUpdated = await updateSetting("custom_plugin.user.toml", custom, enableCustomPlugins, "enable")
+ if (baseUpdated || customUpdated) {
const option = { type: "info", buttons: ["确定", "取消"], title: "preferences", detail: "配置将于重启 Typora 后生效,确认重启?", message: "设置成功" }
const { response } = await this.utils.showMessageBox(option)
if (response === 0) {
@@ -52,35 +45,34 @@ class preferencesPlugin extends BasePlugin {
right_click_menu: "此插件是普通用户调用其他插件的入口",
custom: "所有的二级插件都挂载在此插件上,停用会导致所有的二级插件失效",
json_rpc: "此插件面向开发者",
- ripgrep: "此插件需要您了解 ripgrep 工具",
- test: "此插件面向开发者,建议仅在开发插件期间启用",
reopenClosedFiles: "此插件依赖「标签页管理」插件",
redirectLocalRootUrl: "此插件手动修改配置后才可运行",
article_uploader: "此插件面向特殊人群,手动修改配置后才可运行",
}
- const displayFunc = ([fixedName, plugin]) => ({
+ const display = ([fixedName, plugin]) => ({
label: `${plugin.NAME || plugin.name}(${fixedName})`,
info: INFO[fixedName],
value: fixedName,
checked: plugin.ENABLE || plugin.enable,
disabled: this.config.IGNORE_PLUGINS.includes(fixedName),
})
- const onclick = ev => ev.target.closest("a") && this.utils.runtime.openSettingFolder();
- const [settings, customSettings] = await this.getSettings();
- const plugins = Object.entries(settings).map(displayFunc);
- const customPlugins = Object.entries(customSettings).map(displayFunc);
+ const [base, custom] = await this.getSettings()
+ const basePlugins = Object.entries(base).map(display)
+ const customPlugins = Object.entries(custom).map(display)
+ const onclick = ev => ev.target.closest("a") && this.utils.runtime.openSettingFolder()
const components = [
{ label: "为了保护用户,此处禁止启停部分插件,如需请 修改配置文件", type: "p", onclick },
- { label: "", legend: "一级插件", type: "checkbox", list: plugins },
+ { label: "", legend: "一级插件", type: "checkbox", list: basePlugins },
{ label: "", legend: "二级插件", type: "checkbox", list: customPlugins },
- ];
- const { response, submit: [_, p1, p2] } = await this.utils.dialog.modalAsync({ title: "启停插件", width: "450px", components });
+ ]
+ const modal = { title: "启停插件", width: "450px", components }
+ const { response, submit: [_, _base, _custom] } = await this.utils.dialog.modalAsync(modal)
if (response === 1) {
- await this.togglePlugin(p1, p2, true);
+ await this.togglePlugin(_base, _custom)
}
}
}
module.exports = {
plugin: preferencesPlugin
-};
+}
diff --git a/plugin/search_multi.js b/plugin/search_multi.js
index 0d8a1d8e..5db31db8 100644
--- a/plugin/search_multi.js
+++ b/plugin/search_multi.js
@@ -39,8 +39,8 @@ class searchMultiKeywordPlugin extends BasePlugin {
hotkey = () => [{ hotkey: this.config.HOTKEY, callback: this.call }]
init = () => {
- this.searchHelper = new SearchHelper(this)
- this.highlightHelper = new Highlighter(this)
+ this.searcher = new Searcher(this)
+ this.highlighter = new Highlighter(this)
this.allowedExtensions = new Set(this.config.ALLOW_EXT.map(ext => ext.toLowerCase()))
this.entities = {
modal: document.querySelector("#plugin-search-multi"),
@@ -55,8 +55,8 @@ class searchMultiKeywordPlugin extends BasePlugin {
}
process = () => {
- this.searchHelper.process()
- this.highlightHelper.process()
+ this.searcher.process()
+ this.highlighter.process()
if (this.config.ALLOW_DRAG) {
this.utils.dragFixedModal(this.entities.input, this.entities.modal)
}
@@ -72,7 +72,7 @@ class searchMultiKeywordPlugin extends BasePlugin {
if (!btn) return
const action = btn.getAttribute("action")
if (action === "searchGrammarModal") {
- this.searchHelper.showGrammar()
+ this.searcher.showGrammar()
} else if (action === "toggleCaseSensitive") {
btn.classList.toggle("select")
this.config.CASE_SENSITIVE = !this.config.CASE_SENSITIVE
@@ -122,8 +122,8 @@ class searchMultiKeywordPlugin extends BasePlugin {
if (!input) return
try {
- const ast = this.searchHelper.parse(input)
- const explain = this.searchHelper.toExplain(ast)
+ const ast = this.searcher.parse(input)
+ const explain = this.searcher.toExplain(ast)
this.entities.input.setAttribute("title", explain)
this.utils.notification.hide()
return ast
@@ -139,10 +139,10 @@ class searchMultiKeywordPlugin extends BasePlugin {
ast = ast || this.getAST()
this.utils.hide(this.entities.highlightResult)
if (!ast) return
- const tokens = this.searchHelper.getContentTokens(ast).filter(Boolean)
+ const tokens = this.searcher.getContentTokens(ast).filter(Boolean)
if (!tokens || tokens.length === 0) return
- const hitGroups = this.highlightHelper.doSearch(tokens)
+ const hitGroups = this.highlighter.doSearch(tokens)
const itemList = Object.entries(hitGroups).map(([cls, { name, hits }]) => {
const div = document.createElement("div")
div.className = `plugin-highlight-multi-result-item ${cls}`
@@ -161,7 +161,7 @@ class searchMultiKeywordPlugin extends BasePlugin {
searchMultiByAST = async (rootPath, ast) => {
const { fileFilter, dirFilter } = this._getFilter()
- const matcher = source => this.searchHelper.match(ast, source)
+ const matcher = source => this.searcher.match(ast, source)
const callback = this._showResultItem(rootPath, matcher)
await this._traverseDir(rootPath, fileFilter, dirFilter, callback)
}
@@ -246,7 +246,7 @@ class searchMultiKeywordPlugin extends BasePlugin {
hide = () => {
this.utils.hide(this.entities.modal)
this.utils.hide(this.entities.info)
- this.highlightHelper.clearSearch()
+ this.highlighter.clearSearch()
}
show = () => {
@@ -376,7 +376,7 @@ class QualifierMixin {
* 4. query: Queries the file data to obtain `queryResult`.
* 5. match: Matches `castResult` from step 3 with `queryResult` from step 4.
*/
-class SearchHelper {
+class Searcher {
constructor(plugin) {
this.MIXIN = QualifierMixin
this.config = plugin.config
@@ -414,6 +414,7 @@ class SearchHelper {
default: ({ path, file, stats, buffer }) => `${buffer.toString()}\n${path}`,
path: ({ path, file, stats, buffer }) => path,
file: ({ path, file, stats, buffer }) => file,
+ dir: ({ path, file, stats, buffer }) => this.utils.Package.Path.dirname(path),
ext: ({ path, file, stats, buffer }) => this.utils.Package.Path.extname(file),
content: ({ path, file, stats, buffer }) => buffer.toString(),
time: ({ path, file, stats, buffer }) => this.MIXIN.QUERY.toDate(stats.mtime),
@@ -440,6 +441,7 @@ class SearchHelper {
{ scope: "default", name: "内容或路径", is_meta: false, query: QUERY.default },
{ scope: "path", name: "路径", is_meta: true, query: QUERY.path },
{ scope: "file", name: "文件名", is_meta: true, query: QUERY.file },
+ { scope: "dir", name: "所属目录", is_meta: true, query: QUERY.dir },
{ scope: "ext", name: "扩展名", is_meta: true, query: QUERY.ext },
{ scope: "content", name: "内容", is_meta: false, query: QUERY.content },
{ scope: "frontmatter", name: "FrontMatter", is_meta: false, query: QUERY.frontmatter },
@@ -811,8 +813,8 @@ class SearchHelper {
const genOperator = (...operators) => operators.map(operator => `${operator}
`).join("、")
const genUL = (...li) => `
关键字 | 说明 |
---|---|
空格 | 表示与。文档应该同时满足空格左右两侧的查询条件,等价于 AND |
| | 表示或。文档应该满足 | 左右两侧中至少一个查询条件,等价于 OR |
- | 表示非。文档不可满足 - 右侧的查询条件 |
"" | 表示词组。引号包裹视为词组 |
/RegExp/ | JavaScript 风格的正则表达式 |
scope | 查询属性,用于限定查询条件${scopeDesc} |
operator | 操作符,用于比较查询条件和查询结果${operatorDesc} |
空格 | 连接两个查询条件,表示逻辑与。文档应该同时满足空格左右两侧的查询条件,等价于 AND |
| | 连接两个查询条件,表示逻辑或。文档应该满足 | 左右两侧中至少一个查询条件,等价于 OR |
- | 后接一个查询条件,表示逻辑非。文档不可满足 - 右侧的查询条件 |
"" | 引号包裹文本,表示词组。 |
/regex/ | JavaScript 风格的正则表达式 |
scope | 搜索符,用于限定查询条件${scopeDesc} |
operator | 操作符,用于比较查询关键字和查询结果${operatorDesc} |
() | 小括号,用于调整运算优先级 |