Skip to content

Commit 326e2f0

Browse files
committed
Merge branch 'main' of https://github.com/PKM-er/Pkmer-Math
2 parents 93b52ab + b91cc27 commit 326e2f0

15 files changed

+1422
-0
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/**
2+
* 通用数学公式文本处理框架
3+
* ✅ 适用于 QuickAdd Macro
4+
* ✅ 自动拆分内联数学公式中的非 ASCII 字符
5+
* ✅ 保持数学公式的结构完整
6+
* ✅ 替换后保持原有选中区域
7+
*/
8+
async function processSelectedText(transformFunctions) {
9+
const activeFile = app.workspace.getActiveFile();
10+
if (!activeFile) {
11+
new Notice("No file is currently open.");
12+
return false;
13+
}
14+
const validExtensions = ["md", "canvas"];
15+
if (!validExtensions.includes(activeFile.extension)) {
16+
new Notice("The active file is not a Markdown or Canvas file.");
17+
return false;
18+
}
19+
const editor = app.workspace.activeEditor?.editor;
20+
if (!editor) {
21+
new Notice("No active editor found.");
22+
return false;
23+
}
24+
editor.focus();
25+
let selection = editor.getSelection();
26+
if (!selection || selection.trim() === "") {
27+
new Notice("No text is selected.");
28+
return false;
29+
}
30+
const startPos = editor.getCursor("from");
31+
let modifiedText = selection;
32+
for (const transformFunction of transformFunctions) {
33+
modifiedText = transformFunction(modifiedText);
34+
}
35+
editor.replaceSelection(modifiedText);
36+
const lines = modifiedText.split("\n");
37+
let newEndPos;
38+
if (lines.length === 1) {
39+
newEndPos = { line: startPos.line, ch: startPos.ch + lines[0].length };
40+
} else {
41+
newEndPos = { line: startPos.line + lines.length - 1, ch: lines[lines.length - 1].length };
42+
}
43+
editor.setSelection(startPos, newEndPos);
44+
return true;
45+
}
46+
47+
/**
48+
* transformLatex
49+
* 对数学公式中的非 ASCII 字符进行处理:
50+
* 给未被 \text 包裹的非 ASCII 字符添加 \text 包裹,
51+
* 支持处理内联公式 $...$ 和显示公式 $$...$$。
52+
*
53+
* 注意:对于公式内部已存在的 \text{...} 部分不会重复包装。
54+
*/
55+
function transformLatex(selection) {
56+
return selection.replace(/(\${1,2})([\s\S]+?)\1/g, (match, delimiter, content) => {
57+
// 先将已有的 \text{...} 内容替换为占位符,防止重复包装
58+
let placeholders = [];
59+
let tempContent = content.replace(/\\text\s*\{[^}]*\}/g, (m) => {
60+
placeholders.push(m);
61+
return `%%PLACEHOLDER${placeholders.length - 1}%%`;
62+
});
63+
// 给未被包装的非 ASCII 字符添加 \text 包裹
64+
tempContent = tempContent.replace(/([^\x00-\x7F]+)/g, (m) => `\\text{${m}}`);
65+
// 恢复原有的 \text{...} 内容
66+
tempContent = tempContent.replace(/%%PLACEHOLDER(\d+)%%/g, (m, idx) => placeholders[Number(idx)]);
67+
return delimiter + tempContent + delimiter;
68+
});
69+
}
70+
71+
/**
72+
* cleanEmptyLines
73+
* Markdown 空行优化:删除多余空行,规范化分页符前的空行
74+
*/
75+
function cleanEmptyLines(selection) {
76+
return selection
77+
.replace(/\n+[\t ]*(\n)+(?!\n*(\||-{3,}))/g, '\n')
78+
.replace(/^[\t ]+\n/gm, '\n')
79+
.replace(/\n*(\n-{3,})/g, '\n$1');
80+
}
81+
82+
// QuickAdd Macro 入口:依次执行空行优化与数学公式转换
83+
module.exports = async (params) => {
84+
const processed = await processSelectedText([
85+
cleanEmptyLines, // Markdown 空行优化
86+
transformLatex // 数学公式中非 ASCII 字符包装处理
87+
]);
88+
if (processed) {
89+
new Notice("Text transformation completed.");
90+
}
91+
};
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/**
2+
* 通用文本处理框架
3+
* ✅ 适用于 QuickAdd Macro
4+
* ✅ 可扩展多个文本转换函数
5+
* ✅ 替换文本后保持原有选中区域
6+
*/
7+
async function processSelectedText(transformFunctions) {
8+
const activeFile = app.workspace.getActiveFile();
9+
if (!activeFile) {
10+
new Notice("No file is currently open.");
11+
return;
12+
}
13+
14+
// 允许的文件类型
15+
const validExtensions = ["md", "canvas"];
16+
if (!validExtensions.includes(activeFile.extension)) {
17+
new Notice("The active file is not a Markdown or Canvas file.");
18+
return;
19+
}
20+
21+
// 获取当前活动的编辑器
22+
const editor = app.workspace.activeEditor?.editor;
23+
if (!editor) {
24+
new Notice("No active editor found.");
25+
return;
26+
}
27+
28+
// 确保编辑器处于编辑模式,并获取最新选区
29+
editor.focus();
30+
let selection = editor.getSelection();
31+
if (!selection || selection.trim() === "") {
32+
new Notice("No text is selected.");
33+
return;
34+
}
35+
36+
// 记录原始选区起始位置
37+
const startPos = editor.getCursor("from");
38+
39+
// 依次执行所有传入的文本转换函数
40+
let modifiedText = selection;
41+
for (const transformFunction of transformFunctions) {
42+
modifiedText = transformFunction(modifiedText);
43+
}
44+
45+
// 替换选中的文本
46+
editor.replaceSelection(modifiedText);
47+
48+
// 计算新选区:根据替换后文本计算新结束位置
49+
const lines = modifiedText.split("\n");
50+
let newEndPos;
51+
if (lines.length === 1) {
52+
newEndPos = { line: startPos.line, ch: startPos.ch + lines[0].length };
53+
} else {
54+
newEndPos = { line: startPos.line + lines.length - 1, ch: lines[lines.length - 1].length };
55+
}
56+
57+
// 恢复选区
58+
editor.setSelection(startPos, newEndPos);
59+
}
60+
61+
/**
62+
* 将中文全角标点符号转换为半角符号加空格
63+
*/
64+
function convertPunctuation(content) {
65+
return content
66+
.replace(//g, ", ")
67+
.replace(//g, ". ")
68+
.replace(//g, ": ")
69+
.replace(//g, "; ")
70+
.replace(//g, " (")
71+
.replace(//g, ") ")
72+
.replace(//g, ", ")
73+
.replace(//g, '"')
74+
.replace(//g, '" ');
75+
}
76+
77+
// QuickAdd Macro 入口:转换全角标点符号(保留光标位置)
78+
module.exports = async (params) => {
79+
await processSelectedText([convertPunctuation]);
80+
new Notice("Punctuation conversion accomplished successfully");
81+
};
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/**
2+
* 通用文本处理框架:
3+
* 可扩展多个文本转换函数,替换选中文本后保持原有选区
4+
* 获取选区时先 focus(),替换后恢复选区
5+
* 正确的处理结果提示
6+
* 适用于 QuickAdd Macro
7+
*/
8+
9+
async function processSelectedText(transformFunctions) {
10+
const activeFile = app.workspace.getActiveFile();
11+
if (!activeFile) {
12+
new Notice("No file is currently open.");
13+
return false;
14+
}
15+
16+
// 允许的文件类型
17+
const validExtensions = ["md", "canvas"];
18+
if (!validExtensions.includes(activeFile.extension)) {
19+
new Notice("The active file is not a Markdown or Canvas file.");
20+
return false;
21+
}
22+
23+
// 获取当前活动的编辑器
24+
const editor = app.workspace.activeEditor?.editor;
25+
if (!editor) {
26+
new Notice("No active editor found.");
27+
return false;
28+
}
29+
30+
// Focus 编辑器并获取当前选区
31+
editor.focus();
32+
let selection = editor.getSelection();
33+
if (!selection || selection.trim() === "") {
34+
new Notice("No text is selected.");
35+
return false;
36+
}
37+
38+
// 记录原始选区起始位置
39+
const startPos = editor.getCursor("from");
40+
41+
// 依次执行所有传入的文本转换函数
42+
let modifiedText = selection;
43+
for (const transformFunction of transformFunctions) {
44+
modifiedText = transformFunction(modifiedText);
45+
}
46+
47+
// 替换选中的文本
48+
editor.replaceSelection(modifiedText);
49+
50+
// 根据替换后的文本计算新选区位置
51+
const lines = modifiedText.split("\n");
52+
let newEndPos;
53+
if (lines.length === 1) {
54+
newEndPos = { line: startPos.line, ch: startPos.ch + lines[0].length };
55+
} else {
56+
newEndPos = { line: startPos.line + lines.length - 1, ch: lines[lines.length - 1].length };
57+
}
58+
59+
// 恢复选区
60+
editor.setSelection(startPos, newEndPos);
61+
return true;
62+
}
63+
64+
/**
65+
* ✅ **删除连续多余空行\n和制表符\t**
66+
* ✅ **不会误删表格 `|` 之前的空行**
67+
* ✅ **确保在分页符 `---` 之前至少有一个空行**
68+
*/
69+
function cleanEmptyLines(selection) {
70+
return selection
71+
.replace(/\n+[\t ]*(\n)+(?!\n*(\||-{3,}))/g, '\n')
72+
.replace(/^[\t ]+\n/gm, '\n')
73+
.replace(/\n*(\n-{3,})/g, '\n$1');
74+
}
75+
76+
77+
// QuickAdd Macro 入口:执行文本转换任务
78+
// 如果仅需要删除多余空行,请仅调用 cleanEmptyLines,注释掉 transformLatex
79+
module.exports = async (params) => {
80+
const processed = await processSelectedText([
81+
cleanEmptyLines
82+
]);
83+
if (processed) {
84+
new Notice("Text transformation completed.");
85+
}
86+
};
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* 通用文本处理框架
3+
*/
4+
async function processSelectedText(transformFunction) {
5+
const activeFile = app.workspace.getActiveFile();
6+
if (!activeFile) {
7+
new Notice("No file is currently open.");
8+
return;
9+
}
10+
if (activeFile.extension !== "md") {
11+
new Notice("The active file is not a Markdown file.");
12+
return;
13+
}
14+
const editor = app.workspace.activeEditor?.editor;
15+
if (!editor) {
16+
new Notice("No active editor found.");
17+
return;
18+
}
19+
editor.focus();
20+
let selection = editor.getSelection();
21+
if (!selection || selection.trim() === "") {
22+
new Notice("No text is selected.");
23+
return;
24+
}
25+
const startPos = editor.getCursor("from");
26+
const modifiedText = transformFunction(selection);
27+
editor.replaceSelection(modifiedText);
28+
const lines = modifiedText.split("\n");
29+
let newEndPos;
30+
if (lines.length === 1) {
31+
newEndPos = { line: startPos.line, ch: startPos.ch + lines[0].length };
32+
} else {
33+
newEndPos = { line: startPos.line + lines.length - 1, ch: lines[lines.length - 1].length };
34+
}
35+
editor.setSelection(startPos, newEndPos);
36+
}
37+
38+
/*
39+
* 脚本 2:删除 Markdown 超链接
40+
* 将形如 `[Google](www.google.com)` 的链接替换为仅保留链接文本 "Google"
41+
*/
42+
function removeMarkdownLinks(text) {
43+
return text.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1');
44+
}
45+
46+
// QuickAdd 入口 —— 删除超链接(保留选区)
47+
module.exports = async (params) => {
48+
await processSelectedText(removeMarkdownLinks);
49+
new Notice("Links removed successfully.");
50+
};

_assets_/QuickAdd/Math-LaTeX2MD.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* 通用文本处理框架
3+
* ✅ 适用于 QuickAdd Macro
4+
* ✅ 可扩展多个文本转换函数
5+
* ✅ 替换后保持原有选中区域
6+
*/
7+
async function processSelectedText(transformFunction) {
8+
const activeFile = app.workspace.getActiveFile();
9+
if (!activeFile) {
10+
new Notice("No file is currently open.");
11+
return;
12+
}
13+
const validExtensions = ["md", "canvas"];
14+
if (!validExtensions.includes(activeFile.extension)) {
15+
new Notice("The active file is not a Markdown or Canvas file.");
16+
return;
17+
}
18+
const editor = app.workspace.activeEditor?.editor;
19+
if (!editor) {
20+
new Notice("No active editor found.");
21+
return;
22+
}
23+
editor.focus();
24+
let selection = editor.getSelection();
25+
if (!selection || selection.trim() === "") {
26+
new Notice("No text is selected.");
27+
return;
28+
}
29+
// 记录原始选区起始位置
30+
const startPos = editor.getCursor("from");
31+
const modifiedText = transformFunction(selection);
32+
editor.replaceSelection(modifiedText);
33+
const lines = modifiedText.split("\n");
34+
let newEndPos;
35+
if (lines.length === 1) {
36+
newEndPos = { line: startPos.line, ch: startPos.ch + lines[0].length };
37+
} else {
38+
newEndPos = { line: startPos.line + lines.length - 1, ch: lines[lines.length - 1].length };
39+
}
40+
editor.setSelection(startPos, newEndPos);
41+
}
42+
43+
/*
44+
* 脚本 1:LaTeX2MD —— 将单行公式中的 \( 和 \) 替换为 $,
45+
* 并将多行公式中的 \[ 和 \] 替换为 $$ 。
46+
* 注意:本任务不处理 \left ... \right 结构,也不影响 $$ 块外的文字。
47+
*/
48+
function convertMathDelimiters(text) {
49+
// 替换单行公式:\( 和 \) 替换为 $
50+
let modified = text.replace(/((\\\(\s*)|(\s*\\\)))/g, '$');
51+
// 替换多行公式:\[ 和 \] 替换为 $$
52+
modified = modified.replace(/\\\[(.*?)\\\]/gs, (match, content) => `$$${content}$$`);
53+
return modified;
54+
}
55+
56+
// QuickAdd 入口 —— LaTeX2MD(保留选区)
57+
module.exports = async (params) => {
58+
await processSelectedText(convertMathDelimiters);
59+
new Notice("Math symbols in the selected text have been updated.");
60+
};

0 commit comments

Comments
 (0)