diff --git a/plugin/custom/plugins/markdownLint/index.js b/plugin/custom/plugins/markdownLint/index.js index 8270968d..f1d85831 100644 --- a/plugin/custom/plugins/markdownLint/index.js +++ b/plugin/custom/plugins/markdownLint/index.js @@ -16,8 +16,8 @@ class markdownLintPlugin extends BaseCustomPlugin {
-
+
@@ -47,13 +47,24 @@ class markdownLintPlugin extends BaseCustomPlugin { const _getDetail = (infos = this.errors) => { const obj = infos.map(i => this.utils.fromObject(i, ["lineNumber", "ruleNames", "errorDetail", "errorContext", "errorRange", "fixInfo"])); const content = JSON.stringify(obj.length === 1 ? obj[0] : obj, null, "\t"); - const components = [{ label: "详细信息", type: "textarea", rows: 15, readonly: "readonly", content }]; - this.utils.modal({ title: "格式规范检测", width: "550px", components }); + const components = [{ label: "", type: "textarea", rows: 15, readonly: "readonly", content }]; + this.utils.modal({ title: "详细信息", width: "550px", components }); + } + const _showDoc = () => { + const url = "https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md"; + const onclick = ev => ev.target.closest("a") && this.utils.openUrl(url); + const content = JSON.stringify(this.l10n, null, "\t"); + const components = [ + { label: "以下为简单说明,如需查看完整文档请 前往网站", type: "p", onclick }, + { label: "", type: "textarea", rows: 15, readonly: "readonly", content }, + ]; + this.utils.dialog.modal({ title: "格式规范", width: "600px", components }); } + const _funcMap = { close: () => this.callback(), refresh: () => this.checkLintError(), - doc: () => this.utils.openUrl("https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md"), + doc: () => _showDoc(), toggleSourceMode: () => File.toggleSourceMode(), translate: () => { this.config.translate = !this.config.translate; @@ -61,16 +72,13 @@ class markdownLintPlugin extends BaseCustomPlugin { }, settings: () => { const content = JSON.stringify(this.config.rule_config, null, "\t"); - const components = [{ label: "当前配置", type: "textarea", rows: 15, readonly: "readonly", content }]; - this.utils.modal({ title: "格式规范检测", width: "550px", components }); + const components = [{ label: "", type: "textarea", rows: 15, readonly: "readonly", content }]; + this.utils.modal({ title: "当前配置", width: "550px", components }); }, detailAll: () => _getDetail(this.errors), detailSingle: infoIdx => _getDetail([this.errors[infoIdx]]), fixAll: () => this.fixLintError(), - fixSingle: infoIdx => { - const errors = [this.errors[infoIdx]]; - this.fixLintError(errors); - }, + fixSingle: infoIdx => this.fixLintError([this.errors[infoIdx]]), jumpToLine: lineToGo => { if (!lineToGo) return; if (!File.editor.sourceView.inSourceMode) { diff --git a/plugin/custom/plugins/markdownLint/l10n.js b/plugin/custom/plugins/markdownLint/l10n.js index 0d6613e4..38c79460 100644 --- a/plugin/custom/plugins/markdownLint/l10n.js +++ b/plugin/custom/plugins/markdownLint/l10n.js @@ -1,36 +1,36 @@ module.exports = { - MD001: "标题级别应该逐级递增,不允许跳级", + MD001: "标题级别应该逐级递增,不可跳级", MD002: "第一个标题应该是顶级标题", - MD003: "在标题前加#号来表示标题级别", - MD004: "要求采用一致的无序列表的格式", + MD003: "在标题前加#号以表示标题级别", + MD004: "要求采用一致的无序列表格式", MD005: "要求同级列表项的缩进是一致的", MD006: "最高级标题不能缩进", - MD007: "无序列表嵌套时,使用两个空格缩进", + MD007: "嵌套的无序列表要使用两个空格缩进", MD008: "MD008", - MD009: "行尾最多可以添加两个空格,用于表示换行", + MD009: "行尾最多空两格,以示换行", MD010: "不能使用tab缩进,要使用空格", - MD011: "内联形式的链接的中括号和圆括号使用错误", + MD011: "内联链接的中括号和圆括号使用错误", MD012: "不能有连续的空行", MD013: "行的长度应该在一定范围内", - MD014: "代码块中,终端命令除非后接其输出,否则前面不能有$符号", + MD014: "代码块中的终端命令除非后接其输出,否则不能前接$号", MD015: "MD015", MD016: "MD016", MD017: "MD017", - MD018: "atx标题格式下,#号和文字之间需要一个空格隔开", - MD019: "atx标题格式下,#号和文字之间的空格不能多于一个", - MD020: "closed_atx标题格式下,文字和前后#号之间需用一个空格隔开", - MD021: "closed_atx标题格式下,文字和前后#号之间的空格不能多于一个", - MD022: "标题行的上下行应该都是空行", + MD018: "atx标题格式下,#号和文字之间需空一格", + MD019: "atx标题格式下,#号和文字之间只能空一格", + MD020: "closed_atx标题格式下,文字和#号之间需空一格", + MD021: "closed_atx标题格式下,文字和#号之间只能空一格", + MD022: "标题行的前后需要用空行隔开", MD023: "标题行不能缩进", MD024: "不能连续出现内容重复的标题", MD025: "只能有一个一级标题", MD026: "标题不应以标点符号结尾", - MD027: "引用区块的引用符号和文字之间有且只有一个空格", - MD028: "两个引用区块间不能用空行隔开。引用区块中的空行要用>开头", - MD029: "要求有序列表的序号从1开始,按顺序递增", - MD030: "列表的每一列表项的标识符后只能空一格,后接列表内容", - MD031: "单独的代码块前后需要用空行隔开", - MD032: "列表前后需要用空行隔开,列表的缩进必须一致", + MD027: "引用区块的引用符号和文字之间需空一格", + MD028: "引用区块之间的空行要以>开头", + MD029: "要求有序列表的序号从1开始,按序递增", + MD030: "列表项标识符后只能空一格,后接列表内容", + MD031: "单独的代码块前后需用空行隔开", + MD032: "列表前后需用空行隔开,列表的缩进必须一致", MD033: "不建议使用HTML语句", MD034: "单纯的链接地址需要用尖括号包裹", MD035: "要求采用一致的水平线格式", @@ -39,9 +39,9 @@ module.exports = { MD038: "反引号的内侧不应紧邻空格", MD039: "链接中,中括号的内侧不应紧邻空格", MD040: "代码块应该指定编程语言", - MD041: "文档正文一开始必须是一级标题", + MD041: "文档正文必须以一级标题开始", MD042: "链接的地址不能为空", - MD043: "要求标题遵循一定的结构", + MD043: "要求标题遵循约定的结构", MD044: "大小写错误", MD045: "图片链接必须包含描述文本", MD046: "代码块要用三个反引号包裹", @@ -49,12 +49,12 @@ module.exports = { MD048: "要求采用一致的代码块分隔符", MD049: "要求采用一致的斜体格式", MD050: "要求采用一致的加粗格式", - MD051: "文内链接必须有效,不能指向一个不存在的标题", - MD052: "引用链接和图片应该使用已经定义的标签", + MD051: "文内链接必须有效", + MD052: "引用链接和图片应使用已经定义的标签", MD053: "链接和图片引用定义不可省略", MD054: "要求采用一致的链接和图片格式", MD055: "要求采用一致的表格分隔符格式", - MD056: "表格列数要求是一致的,不能省略或多余", + MD056: "表格列数必须一致的", MD057: "MD057", MD058: "表格前后需要用空行隔开", }; \ No newline at end of file diff --git a/plugin/custom/plugins/resourceOperation.js b/plugin/custom/plugins/resourceOperation.js index 61d808b9..b6bf3c91 100644 --- a/plugin/custom/plugins/resourceOperation.js +++ b/plugin/custom/plugins/resourceOperation.js @@ -42,12 +42,12 @@ class resourceOperationPlugin extends BaseCustomPlugin { this.regexp = ignore_img_html_element ? new RegExp("!\\[.*?\\]\\((?.*)\\)", "g") : new RegExp("!\\[.*?\\]\\((?.*)\\)|.*?)\"", "g") - this.resourceSuffix = new Set(resource_suffix); - this.fileSuffix = new Set(markdown_suffix); + this.resourceSuffixs = new Set(resource_suffix); + this.fileSuffixs = new Set(markdown_suffix); this.resources = new Set(); this.resourcesInFile = new Set(); if (collect_file_without_suffix) { - this.resourceSuffix.add(""); + this.resourceSuffixs.add(""); } this.nonExistInFile = null; this.nonExistInFolder = null; @@ -77,10 +77,10 @@ class resourceOperationPlugin extends BaseCustomPlugin { if (!target) return; const tr = target.closest("tr"); if (!tr) return; - const p = tr.querySelector(".plugin-resource-operation-src"); - if (!p) return; + const img = tr.querySelector("img"); + if (!img) return; - const src = p.dataset.path; + const src = img.getAttribute("src"); const action = target.getAttribute("action"); if (action === "delete") { if (this.showWarnDialog) { @@ -126,21 +126,22 @@ class resourceOperationPlugin extends BaseCustomPlugin { delete output.resource_non_exist_in_file; delete output.resource_non_exist_in_folder; const replacer = (key, value) => Array.isArray(value) ? value.join("|") : value + const btnGroup = `
` - const rows = Array.from(this.nonExistInFile, (row, idx) => `${idx + 1}${row}${btnGroup}`) + const nonExistInFile = Array.from(this.nonExistInFile, (row, idx) => `${idx + 1}${row}${btnGroup}`) + const nonExistInFolder = Array.from(this.nonExistInFolder, (row, idx) => `${idx + 1}${row}`) + this.entities.wrap.innerHTML = ` - +
- - ${rows.join("")} + + ${nonExistInFile.join("")}
存在于文件夹但不存在于md文件的资源(共${this.nonExistInFile.size}项)
#resourceoperation
#resourcepreviewoperation
- - ${Array.from(this.nonExistInFolder, (row, idx) => ``).join("")} + ${nonExistInFolder.join("")}
存在于md文件但不存在于文件夹的资源(共${this.nonExistInFolder.size}项)
#resource
${idx + 1}${row}
-
配置
` @@ -157,8 +158,10 @@ class resourceOperationPlugin extends BaseCustomPlugin { togglePreview = force => { const icon = this.entities.iconGroup.querySelector('[action="togglePreview"]'); const wantClose = force === false || icon.classList.contains("ion-eye"); + this.entities.wrap.querySelectorAll(".non-exist-in-file-table td:nth-of-type(3), .non-exist-in-file-table th:nth-of-type(3)") + .forEach(e => e.classList.toggle("plugin-common-hidden", wantClose)); const func = wantClose ? "off" : "on"; - const className = ".plugin-resource-operation-src"; + const className = "img"; this.entities.$wrap [func]("mouseover", className, this._showPopup) [func]("mouseout", className, this._hidePopup) @@ -171,7 +174,7 @@ class resourceOperationPlugin extends BaseCustomPlugin { const popup = this.entities.popup; if (!popup) return; - popup.src = ev.target.dataset.path; + popup.src = ev.target.getAttribute("src"); const left = Math.min(window.innerWidth - 10 - popup.offsetWidth, ev.clientX + 10); const top = Math.min(window.innerHeight - 50 - popup.offsetHeight, ev.clientY + 20); popup.style.left = `${left}px`; @@ -182,8 +185,8 @@ class resourceOperationPlugin extends BaseCustomPlugin { getOutput = () => ({ search_folder: this.utils.getMountFolder(), - resource_suffix: Array.from(this.resourceSuffix), - markdown_suffix: Array.from(this.fileSuffix), + resource_suffix: Array.from(this.resourceSuffixs), + markdown_suffix: Array.from(this.fileSuffixs), ignore_img_html_element: this.config.ignore_img_html_element, collect_file_without_suffix: this.config.collect_file_without_suffix, ignore_folders: this.config.ignore_folders, @@ -265,40 +268,39 @@ Designed with ♥ by [obgnail](https://github.com/obgnail/typora_plugin) return imagePath; } - const collectMatch = async content => { - for (const match of content.matchAll(this.regexp)) { + const collectMatch = content => { + return Promise.all(content.matchAll(this.regexp).map(async match => { let src = match.groups.src1 || match.groups.src2; - if (!src || isNetworkImage(src) || isSpecialImage(src)) continue; + if (!src || isNetworkImage(src) || isSpecialImage(src)) return; try { - src = decodeURI(src).split("?")[0]; + src = decodeURIComponent(src).split("?")[0]; } catch (e) { console.warn("error path:", src); - continue + return; } src = resolve(dir, src); - if (this.resourcesInFile.has(src)) continue; + if (this.resourcesInFile.has(src)) return; const resourcePath = await getRealPath(src); - this.resourcesInFile.add(resourcePath); - + if (this.resourceSuffixs.has(extname(resourcePath).toLowerCase())) { + this.resourcesInFile.add(resourcePath); + } const remain = src.slice(resourcePath.length); if (remain) { await collectMatch(remain + ")"); } - } + })) } const ext = extname(filePath).toLowerCase(); - if (this.resourceSuffix.has(ext)) { + if (this.resourceSuffixs.has(ext)) { this.resources.add(filePath); - return + } else if (this.fileSuffixs.has(ext)) { + const buffer = await readFile(filePath); + await collectMatch(buffer.toString()); } - if (!this.fileSuffix.has(ext)) return; - - const buffer = await readFile(filePath); - await collectMatch(buffer.toString()); } traverseDir = async (dir, callback) => { @@ -307,15 +309,15 @@ Designed with ♥ by [obgnail](https://github.com/obgnail/typora_plugin) async function traverse(dir) { const files = await readdir(dir); - for (const file of files) { + await Promise.all(files.map(async file => { const filePath = join(dir, file); const stats = await stat(filePath); if (stats.isFile()) { - await callback(filePath, dir) + await callback(filePath, dir); } else if (stats.isDirectory() && !ignore_folders.includes(file)) { await traverse(filePath); } - } + })) } await traverse(dir); diff --git a/plugin/file_counter.js b/plugin/file_counter.js index d317a6c5..30c1dac8 100644 --- a/plugin/file_counter.js +++ b/plugin/file_counter.js @@ -65,11 +65,10 @@ class fileCounterPlugin extends BasePlugin { async function traverse(dir) { const stats = await promises.stat(dir); - if (!stats.isDirectory()) { - return; - } + if (!stats.isDirectory()) return; + const files = await promises.readdir(dir); - for (const file of files) { + await Promise.all(files.map(async file => { const filePath = Path.join(dir, file); const fileStats = await promises.stat(filePath); if (fileStats.isFile() && fileFilter(filePath, fileStats)) { @@ -78,7 +77,7 @@ class fileCounterPlugin extends BasePlugin { if (fileStats.isDirectory() && dirFilter(file)) { await traverse(filePath); } - } + })) } traverse(dir).then(() => then(fileCount)).catch(err => console.error(err)); diff --git a/plugin/global/styles/resourceOperation.css b/plugin/global/styles/resourceOperation.css index b76a59c0..f12ba0fa 100644 --- a/plugin/global/styles/resourceOperation.css +++ b/plugin/global/styles/resourceOperation.css @@ -12,8 +12,8 @@ } #plugin-resource-operation .plugin-resource-operation-popup { - max-width: 400px; - max-height: 400px; + max-width: 500px; + max-height: 500px; position: fixed; box-shadow: 0 0 0.125em 0 rgba(0, 0, 0, .5); border-radius: 0.5em; @@ -21,7 +21,26 @@ z-index: 9999; } -#plugin-resource-operation td { +.non-exist-in-file-table th:first-child { + width: 10px; +} +.non-exist-in-file-table th:last-child { + width: 150px; +} +.non-exist-in-file-table td:nth-of-type(2) { + max-width: 200px; + white-space: break-spaces; + word-break: break-word; +} +.non-exist-in-file-table td:nth-of-type(3) { + max-width: 100px; + max-height: 100px; +} +.non-exist-in-file-table td:last-of-type { + text-align: center; +} + +#plugin-resource-operation tbody { font-size: 0.7em; } diff --git a/plugin/search_multi.js b/plugin/search_multi.js index bd2ae589..e3ba0725 100644 --- a/plugin/search_multi.js +++ b/plugin/search_multi.js @@ -140,15 +140,16 @@ class searchMultiKeywordPlugin extends BasePlugin { async function traverse(dir) { const files = await readdir(dir); - for (const file of files) { + await Promise.all(files.map(async file => { const filePath = Path.join(dir, file); const stats = await stat(filePath); if (stats.isFile() && (!fileFilter || fileFilter(filePath, stats))) { - readFile(filePath).then(buffer => callback(filePath, stats, buffer)).catch(console.error); + const buffer = await readFile(filePath); + callback(filePath, stats, buffer); } else if (stats.isDirectory() && (!dirFilter || dirFilter(file))) { await traverse(filePath); } - } + })) } traverse(dir).then(then).catch(console.error);