mirror of
https://github.com/moyf/easy-copy.git
synced 2026-07-22 12:20:26 +00:00
Add more replication type matches.
This commit is contained in:
parent
d511d8c263
commit
ee75e93fc3
4 changed files with 457 additions and 322 deletions
50
src/i18n.ts
50
src/i18n.ts
|
|
@ -7,16 +7,19 @@ export enum Language {
|
|||
|
||||
// 定义翻译键值类型
|
||||
export type TranslationKey =
|
||||
| 'no-file' | 'no-content' | 'inline-code-copied' | 'block-id-copied'
|
||||
| 'note-link-copied' | 'heading-copied' | 'format'
|
||||
| 'add-to-menu' | 'add-to-menu-desc' | 'show-notice' | 'show-notice-desc'
|
||||
| 'use-heading-as-display' | 'use-heading-as-display-desc' | 'link-format'
|
||||
| 'link-format-desc' | 'markdown-link' | 'wiki-link' | 'contextual-copy'
|
||||
| 'target' | 'enable-bold' | 'enable-bold-desc' | 'enable-highlight'
|
||||
| 'enable-highlight-desc' | 'enable-italic' | 'enable-italic-desc'
|
||||
| 'bold-copied' | 'highlight-copied' | 'italic-copied'
|
||||
| 'customize-targets' | 'customize-targets-desc' | 'enable-inline-code'
|
||||
| 'enable-inline-code-desc';
|
||||
| 'no-file' | 'no-content'
|
||||
| 'inline-code-copied' | 'block-id-copied' | 'note-link-copied' | 'heading-copied' | 'strikethrough-copied' | 'inline-latex-copied' | 'link-copied' | 'bold-copied' | 'highlight-copied' | 'italic-copied'
|
||||
| 'format' | 'add-to-menu' | 'add-to-menu-desc' | 'show-notice' | 'show-notice-desc'
|
||||
| 'use-heading-as-display' | 'use-heading-as-display-desc'
|
||||
| 'link-format'| 'link-format-desc' | 'markdown-link' | 'wiki-link' | 'contextual-copy'
|
||||
| 'target' | 'customize-targets' | 'customize-targets-desc'
|
||||
| 'enable-bold' | 'enable-bold-desc'
|
||||
| 'enable-highlight'| 'enable-highlight-desc'
|
||||
| 'enable-italic' | 'enable-italic-desc'
|
||||
| 'enable-inline-code' | 'enable-inline-code-desc'
|
||||
| 'enable-strikethrough' | 'enable-strikethrough-desc'
|
||||
| 'enable-inline-latex' | 'enable-inline-latex-desc'
|
||||
| 'enable-link' | 'enable-link-desc';
|
||||
|
||||
// 本地化翻译字典
|
||||
export const translations: Record<Language, Record<TranslationKey, string>> = {
|
||||
|
|
@ -31,6 +34,9 @@ export const translations: Record<Language, Record<TranslationKey, string>> = {
|
|||
'bold-copied': 'Bold text copied!',
|
||||
'highlight-copied': 'Highlighted text copied!',
|
||||
'italic-copied': 'Italic text copied!',
|
||||
'inline-latex-copied': 'Inline LaTeX copied!',
|
||||
'strikethrough-copied': 'Strikethrough text copied!',
|
||||
'link-copied': 'Link copied!',
|
||||
|
||||
// 设置界面
|
||||
'format': 'Format',
|
||||
|
|
@ -55,6 +61,12 @@ export const translations: Record<Language, Record<TranslationKey, string>> = {
|
|||
'enable-highlight-desc': 'Enable copying highlighted text like ==highlight example==',
|
||||
'enable-italic': 'Enable italic text',
|
||||
'enable-italic-desc': 'Enable copying italic text like *italic example*',
|
||||
'enable-strikethrough': 'Enable strikethrough text',
|
||||
'enable-strikethrough-desc': 'Enable copying strikethrough text like ~~strikethrough example~~',
|
||||
'enable-inline-latex': 'Enable inline LaTeX',
|
||||
'enable-inline-latex-desc': 'Enable copying inline LaTeX like $latex example$',
|
||||
'enable-link': 'Enable link',
|
||||
'enable-link-desc': 'Enable copying link like [linktitle example](linkurl example)',
|
||||
|
||||
// 命令名称
|
||||
'contextual-copy': 'Contextual Copy'
|
||||
|
|
@ -70,6 +82,9 @@ export const translations: Record<Language, Record<TranslationKey, string>> = {
|
|||
'bold-copied': '加粗文本已复制!',
|
||||
'highlight-copied': '高亮文本已复制!',
|
||||
'italic-copied': '斜体文本已复制!',
|
||||
'inline-latex-copied': '行内LaTeX已复制!',
|
||||
'strikethrough-copied': '删除线文本已复制!',
|
||||
'link-copied': '链接已复制!',
|
||||
|
||||
// 设置界面
|
||||
'format': '格式',
|
||||
|
|
@ -94,6 +109,12 @@ export const translations: Record<Language, Record<TranslationKey, string>> = {
|
|||
'enable-highlight-desc': '启用复制高亮文本,如 ==高亮示例==',
|
||||
'enable-italic': '启用斜体文本',
|
||||
'enable-italic-desc': '启用复制斜体文本,如 *斜体示例*',
|
||||
'enable-strikethrough': '启用删除线文本',
|
||||
'enable-strikethrough-desc': '启用复制删除线文本,如 ~~删除线示例~~',
|
||||
'enable-inline-latex': '启用行内LaTeX',
|
||||
'enable-inline-latex-desc': '启用复制行内LaTeX,如 $latex 示例$',
|
||||
'enable-link': '启用链接',
|
||||
'enable-link-desc': '启用复制链接,如 [链接标题 示例](链接地址 示例)',
|
||||
|
||||
// 命令名称
|
||||
'contextual-copy': '智能复制'
|
||||
|
|
@ -109,6 +130,9 @@ export const translations: Record<Language, Record<TranslationKey, string>> = {
|
|||
'bold-copied': '加粗文本已複製!',
|
||||
'highlight-copied': '高亮文本已複製!',
|
||||
'italic-copied': '斜體文本已複製!',
|
||||
'inline-latex-copied': '行內LaTeX已複製!',
|
||||
'strikethrough-copied': '刪除線文本已複製!',
|
||||
'link-copied': '連結已複製!',
|
||||
|
||||
// 設置界面
|
||||
'format': '格式',
|
||||
|
|
@ -133,6 +157,12 @@ export const translations: Record<Language, Record<TranslationKey, string>> = {
|
|||
'enable-highlight-desc': '啟用複製高亮文本,如 ==高亮示例==',
|
||||
'enable-italic': '啟用斜體文本',
|
||||
'enable-italic-desc': '啟用複製斜體文本,如 *斜體示例*',
|
||||
'enable-strikethrough': '啟用刪除線文本',
|
||||
'enable-strikethrough-desc': '啟用複製刪除線文本,如 ~~刪除線示例~~',
|
||||
'enable-inline-latex': '啟用行內LaTeX',
|
||||
'enable-inline-latex-desc': '啟用複製行內LaTeX,如 $latex 示例$',
|
||||
'enable-link': '啟用連結',
|
||||
'enable-link-desc': '啟用複製連結,如 [連結標題 示例](連結地址 示例)',
|
||||
|
||||
// 命令名称
|
||||
'contextual-copy': '智能複製'
|
||||
|
|
|
|||
523
src/main.ts
523
src/main.ts
|
|
@ -1,34 +1,7 @@
|
|||
import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting, Menu } from 'obsidian';
|
||||
import { Editor, MarkdownView, Notice, Plugin, Menu } from 'obsidian';
|
||||
import { Language, TranslationKey, I18n } from './i18n';
|
||||
|
||||
enum LinkFormat {
|
||||
MDLINK = 'markdown-link',
|
||||
WIKILINK = 'wiki-link'
|
||||
}
|
||||
|
||||
interface EasyCopySettings {
|
||||
addToMenu: boolean;
|
||||
showNotice: boolean;
|
||||
useHeadingAsDisplayText: boolean;
|
||||
linkFormat: LinkFormat;
|
||||
customizeTargets: boolean;
|
||||
enableInlineCode: boolean;
|
||||
enableBold: boolean;
|
||||
enableHighlight: boolean;
|
||||
enableItalic: boolean;
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS: EasyCopySettings = {
|
||||
addToMenu: true,
|
||||
showNotice: true,
|
||||
useHeadingAsDisplayText: true,
|
||||
linkFormat: LinkFormat.WIKILINK,
|
||||
customizeTargets: false,
|
||||
enableInlineCode: true,
|
||||
enableBold: true,
|
||||
enableHighlight: true,
|
||||
enableItalic: false
|
||||
}
|
||||
import { ContextData, ContextType, DEFAULT_SETTINGS, EasyCopySettings, LinkFormat } from './type';
|
||||
import { EasyCopySettingTab } from './settingTab';
|
||||
|
||||
export default class EasyCopy extends Plugin {
|
||||
settings: EasyCopySettings;
|
||||
|
|
@ -115,6 +88,140 @@ export default class EasyCopy extends Plugin {
|
|||
return this.i18n.t(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前行的上下文类型
|
||||
* @param editor
|
||||
* @param view
|
||||
* @returns ContextData
|
||||
*/
|
||||
private determineContextType(editor: Editor, view: MarkdownView): ContextData {
|
||||
// 获取当前文件和光标信息
|
||||
const file = view.file;
|
||||
if (!file) {
|
||||
new Notice(this.t('no-file'));
|
||||
return { type: ContextType.NULL, curLine: '', match: null, range: null };
|
||||
}
|
||||
|
||||
const cursor = editor.getCursor();
|
||||
const curLine = editor.getLine(cursor.line);
|
||||
const curCh = cursor.ch;
|
||||
|
||||
// 根据光标位置解析内容类型
|
||||
const beforeCursor = curLine.slice(0, curCh); // 光标前的内容
|
||||
const afterCursor = curLine.slice(curCh); // 光标后的内容
|
||||
|
||||
// 匹配优先级:加粗 > 斜体 > 高亮 > 删除线 > 行内代码 > 行内Latex > 块ID
|
||||
const matchers = [
|
||||
{ type: ContextType.BOLD, regex: /\*\*([^*]+)\*\*/g , enable: this.settings.enableBold},
|
||||
// 使用前后断言 (?<!\*) 和 (?!\*),确保 * 不被包裹在 ** 中。
|
||||
{ type: ContextType.ITALIC, regex: /(?<!\*)\*([^*]+)\*(?!\*)/g , enable: this.settings.customizeTargets && this.settings.enableItalic},
|
||||
{ type: ContextType.HIGHLIGHT, regex: /==([^=]+)==/g , enable: this.settings.customizeTargets && this.settings.enableHighlight},
|
||||
{ type: ContextType.STRIKETHROUGH, regex: /~~([^~]+)~~/g , enable: this.settings.customizeTargets && this.settings.enableStrikethrough},
|
||||
{ type: ContextType.INLINECODE, regex: /`([^`]+)`/g , enable: this.settings.customizeTargets && this.settings.enableInlineCode},
|
||||
{ type: ContextType.INLINELATEX, regex: /\$([^$]*)\$/g , enable: this.settings.customizeTargets && this.settings.enableInlineLatex},
|
||||
{ type: ContextType.BLOCKID, regex: /\^([a-zA-Z0-9_-]+)/g , enable: true}, // 块ID不需要判断enable
|
||||
];
|
||||
|
||||
for (const matcher of matchers) {
|
||||
if (!matcher.enable) continue; // 如果当前类型未启用,则跳过
|
||||
const matchInfo = this.getMatchInfo(beforeCursor, afterCursor, matcher.regex);
|
||||
if (matchInfo) {
|
||||
return {
|
||||
type: matcher.type,
|
||||
curLine,
|
||||
match: matchInfo.content, // 返回内容,不包括语法
|
||||
range: matchInfo.range,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 检测链接
|
||||
if (this.settings.customizeTargets && this.settings.enableLink) {
|
||||
const linkInfo = this.isCursorInLink(beforeCursor, afterCursor);
|
||||
if (linkInfo) {
|
||||
return {
|
||||
type: linkInfo.type,
|
||||
curLine,
|
||||
match: linkInfo.content,
|
||||
range: linkInfo.range,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 如果当前行是标题行,且光标不在其他 Markdown 语法范围内,则返回标题类型
|
||||
const headingRegex = /^(#+)\s/; // 标题正则表达式
|
||||
if (headingRegex.test(curLine)) {
|
||||
const match = curLine.match(headingRegex);
|
||||
const headingContent = curLine.replace(headingRegex, '').trim();
|
||||
return {
|
||||
type: ContextType.HEADING,
|
||||
curLine,
|
||||
match: headingContent, // 返回内容,不包括语法
|
||||
range: match ? [match[0].length, curLine.length] : null,
|
||||
};
|
||||
}
|
||||
|
||||
// 默认返回空值
|
||||
return { type: ContextType.NULL, curLine, match: null, range: null };
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取光标所在的匹配信息
|
||||
* @param beforeCursor 光标前的文本
|
||||
* @param afterCursor 光标后的文本
|
||||
* @param regex 匹配的正则表达式
|
||||
* @returns 匹配信息,包括匹配内容和范围
|
||||
*/
|
||||
private getMatchInfo(beforeCursor: string, afterCursor: string, regex: RegExp): { content: string; range: [number, number] } | null {
|
||||
let match;
|
||||
while ((match = regex.exec(beforeCursor + afterCursor)) !== null) {
|
||||
const matchStart = match.index;
|
||||
const matchEnd = match.index + match[0].length;
|
||||
|
||||
// 判断光标是否在匹配范围内
|
||||
if (beforeCursor.length >= matchStart && beforeCursor.length <= matchEnd) {
|
||||
return {
|
||||
content: match[1], // 返回内容,不包括语法
|
||||
range: [matchStart, matchEnd],
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private isCursorInLink(beforeCursor: string, afterCursor: string): {type: ContextType.LINKTITLE | ContextType.LINEURL, content: string, range: [number, number]} | null {
|
||||
// 匹配链接的正则表达式
|
||||
const linkRegex = /\[([^\]]*?)\]\(([^)]*?)\)/g;
|
||||
|
||||
const fullText = beforeCursor + afterCursor;
|
||||
let match: RegExpExecArray | null;
|
||||
while ((match = linkRegex.exec(fullText)) !== null) {
|
||||
const linkStart = match.index; // 链接的起始位置
|
||||
const linkEnd = linkStart + match[0].length; // 链接的结束位置
|
||||
|
||||
// 光标位置
|
||||
const cursorPos = beforeCursor.length;
|
||||
|
||||
// 判断光标是否在当前链接范围内
|
||||
if (cursorPos >= linkStart && cursorPos <= linkEnd) {
|
||||
const bracketStart = linkStart + 1; // `[` 后的位置
|
||||
const bracketEnd = linkStart + match[1].length + 1; // `]` 的位置
|
||||
const parenStart = bracketEnd + 2; // `(` 后的位置
|
||||
const parenEnd = parenStart + match[2].length; // `)` 的位置
|
||||
|
||||
// 判断光标在 [] 还是 ()
|
||||
if (cursorPos >= bracketStart && cursorPos <= bracketEnd) {
|
||||
return { type: ContextType.LINKTITLE, content: match[1], range: [bracketStart, bracketEnd] };
|
||||
} else if (cursorPos >= parenStart && cursorPos <= parenEnd) {
|
||||
return { type: ContextType.LINEURL, content: match[2], range: [parenStart, parenEnd] };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果光标不在任何链接中,返回 null
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 智能复制功能:根据光标位置复制不同类型的内容
|
||||
* 支持复制行内代码、块ID链接和标题链接
|
||||
|
|
@ -126,141 +233,111 @@ export default class EasyCopy extends Plugin {
|
|||
new Notice(this.t('no-file'));
|
||||
return;
|
||||
}
|
||||
|
||||
const cursor = editor.getCursor();
|
||||
const curLine = editor.getLine(cursor.line);
|
||||
const curCh = cursor.ch;
|
||||
|
||||
// 获取文件名(去除.md后缀)
|
||||
const filename = file.basename;
|
||||
|
||||
// 1. 检查是否是标题行
|
||||
if (curLine.startsWith('#')) {
|
||||
this.copyHeadingLink(curLine, filename);
|
||||
|
||||
// 获取当前行的上下文类型
|
||||
const contextType = this.determineContextType(editor, view);
|
||||
console.log('contextType:', contextType);
|
||||
if (contextType.type == ContextType.NULL) {
|
||||
new Notice(this.t('no-content'));
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 检查是否包含行内代码
|
||||
if ((!this.settings.customizeTargets || this.settings.enableInlineCode) && curLine.includes('`')) {
|
||||
// 尝试提取行内代码
|
||||
const inlineCode = this.getInlineCode(curLine, curCh);
|
||||
if (inlineCode) {
|
||||
navigator.clipboard.writeText(inlineCode);
|
||||
if (this.settings.showNotice) {
|
||||
new Notice(this.t('inline-code-copied'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 检查是否包含块ID
|
||||
if (curLine.includes('^')) {
|
||||
// 尝试提取块ID
|
||||
const blockId = this.getBlockId(curLine);
|
||||
if (blockId) {
|
||||
const blockIdLink = this.settings.linkFormat === LinkFormat.WIKILINK
|
||||
? `[[${filename}#^${blockId}]]`
|
||||
: `[${blockId}](${filename}#^${blockId})`;
|
||||
|
||||
navigator.clipboard.writeText(blockIdLink);
|
||||
if (this.settings.showNotice) {
|
||||
new Notice(this.t('block-id-copied'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 检查是否包含加粗文本
|
||||
if ((!this.settings.customizeTargets || this.settings.enableBold) && curLine.includes('**')) {
|
||||
const boldText = this.getBoldText(curLine, curCh);
|
||||
if (boldText) {
|
||||
navigator.clipboard.writeText(boldText);
|
||||
|
||||
switch (contextType.type) {
|
||||
case ContextType.BOLD:
|
||||
navigator.clipboard.writeText(contextType.match!);
|
||||
if (this.settings.showNotice) {
|
||||
new Notice(this.t('bold-copied'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 检查是否包含高亮文本
|
||||
if ((!this.settings.customizeTargets || this.settings.enableHighlight) && curLine.includes('==')) {
|
||||
const highlightText = this.getHighlightText(curLine, curCh);
|
||||
if (highlightText) {
|
||||
navigator.clipboard.writeText(highlightText);
|
||||
if (this.settings.showNotice) {
|
||||
new Notice(this.t('highlight-copied'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 检查是否包含斜体文本
|
||||
if ((!this.settings.customizeTargets || this.settings.enableItalic) && (curLine.includes('*') || curLine.includes('_'))) {
|
||||
const italicText = this.getItalicText(curLine, curCh);
|
||||
if (italicText) {
|
||||
navigator.clipboard.writeText(italicText);
|
||||
case ContextType.ITALIC:
|
||||
navigator.clipboard.writeText(contextType.match!);
|
||||
if (this.settings.showNotice) {
|
||||
new Notice(this.t('italic-copied'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
case ContextType.HIGHLIGHT:
|
||||
navigator.clipboard.writeText(contextType.match!);
|
||||
if (this.settings.showNotice) {
|
||||
new Notice(this.t('highlight-copied'));
|
||||
}
|
||||
return;
|
||||
case ContextType.STRIKETHROUGH:
|
||||
navigator.clipboard.writeText(contextType.match!);
|
||||
if (this.settings.showNotice) {
|
||||
new Notice(this.t('strikethrough-copied'));
|
||||
}
|
||||
return;
|
||||
case ContextType.INLINECODE:
|
||||
navigator.clipboard.writeText(contextType.match!);
|
||||
if (this.settings.showNotice) {
|
||||
new Notice(this.t('inline-code-copied'));
|
||||
}
|
||||
return;
|
||||
case ContextType.INLINELATEX:
|
||||
navigator.clipboard.writeText(contextType.match!);
|
||||
if (this.settings.showNotice) {
|
||||
new Notice(this.t('inline-latex-copied'));
|
||||
}
|
||||
return;
|
||||
|
||||
case ContextType.LINKTITLE:
|
||||
case ContextType.LINEURL:
|
||||
// 复制链接标题或链接地址
|
||||
navigator.clipboard.writeText(contextType.match!);
|
||||
if (this.settings.showNotice) {
|
||||
new Notice(this.t('link-copied'));
|
||||
}
|
||||
return;
|
||||
case ContextType.BLOCKID:
|
||||
{
|
||||
const blockIdLink = this.settings.linkFormat === LinkFormat.WIKILINK
|
||||
? `[[${filename}#^${contextType.match!}]]`
|
||||
: `[${contextType.match!}](${filename}#^${contextType.match!})`;
|
||||
navigator.clipboard.writeText(blockIdLink);
|
||||
}
|
||||
if (this.settings.showNotice) {
|
||||
new Notice(this.t('block-id-copied'));
|
||||
}
|
||||
return;
|
||||
case ContextType.HEADING:
|
||||
this.copyHeadingLink(contextType.match!, filename);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// 如果没有找到可复制的内容
|
||||
new Notice(this.t('no-content'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取行内代码
|
||||
*/
|
||||
private getInlineCode(str: string, cursor: number): string | null {
|
||||
const start = str.lastIndexOf('`', cursor - 1);
|
||||
const end = str.indexOf('`', cursor);
|
||||
|
||||
if (start === -1 || end === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return str.substring(start + 1, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取块ID
|
||||
*/
|
||||
private getBlockId(str: string): string | null {
|
||||
// 提取当前光标所在行的像是 ^block-id-123 这样的 block id
|
||||
const regex = /(?:^|\s+)\^([a-zA-Z0-9_-]+)(?:\s*|$)/;
|
||||
const match = str.match(regex);
|
||||
|
||||
if (match) {
|
||||
return match[1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制标题链接
|
||||
*/
|
||||
private copyHeadingLink(headingLine: string, filename: string): void {
|
||||
private copyHeadingLink(content: string, filename: string): void {
|
||||
// 提取标题文本和级别
|
||||
const selectedHeading = headingLine.replace(/#+\s+/, "#");
|
||||
const linkAlias = selectedHeading.replace(/#+\s*/, "");
|
||||
|
||||
let selectedHeading = content;
|
||||
// 如果内容是[[内容]],移除[[]]
|
||||
if (selectedHeading.startsWith('[[') && selectedHeading.endsWith(']]')) {
|
||||
selectedHeading = selectedHeading.slice(2, -2);
|
||||
}
|
||||
const linkAlias = selectedHeading;
|
||||
let noteFlag = 0;
|
||||
|
||||
let headingReferenceLink = "";
|
||||
|
||||
// 根据设置选择链接格式
|
||||
if (this.settings.linkFormat === LinkFormat.WIKILINK) {
|
||||
// Wiki链接格式
|
||||
headingReferenceLink = `[[${filename}${selectedHeading}|${linkAlias}]]`;
|
||||
headingReferenceLink = `[[${filename}#${selectedHeading}|${linkAlias}]]`;
|
||||
|
||||
// 处理同名标题的特殊情况
|
||||
if (this.settings.useHeadingAsDisplayText && filename === linkAlias) {
|
||||
headingReferenceLink = `[[${filename}]]`;
|
||||
noteFlag = 1;
|
||||
}
|
||||
} else {
|
||||
// Markdown链接格式
|
||||
headingReferenceLink = `[${linkAlias}](${filename}${selectedHeading})`;
|
||||
headingReferenceLink = `[${linkAlias}](${filename}#${selectedHeading})`;
|
||||
}
|
||||
|
||||
// 复制到剪贴板
|
||||
|
|
@ -268,7 +345,7 @@ export default class EasyCopy extends Plugin {
|
|||
|
||||
// 显示通知
|
||||
if (this.settings.showNotice) {
|
||||
if (headingLine.startsWith("# ")) {
|
||||
if (noteFlag) {
|
||||
new Notice(this.t('note-link-copied'));
|
||||
} else {
|
||||
new Notice(this.t('heading-copied'));
|
||||
|
|
@ -276,68 +353,6 @@ export default class EasyCopy extends Plugin {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取加粗文本
|
||||
*/
|
||||
private getBoldText(str: string, cursor: number): string | null {
|
||||
// 查找光标前后的 ** 标记
|
||||
const start = str.lastIndexOf('**', cursor - 1);
|
||||
const end = str.indexOf('**', cursor);
|
||||
|
||||
if (start === -1 || end === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return str.substring(start + 2, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取高亮文本
|
||||
*/
|
||||
private getHighlightText(str: string, cursor: number): string | null {
|
||||
// 查找光标前后的 == 标记
|
||||
const start = str.lastIndexOf('==', cursor - 1);
|
||||
const end = str.indexOf('==', cursor);
|
||||
|
||||
if (start === -1 || end === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return str.substring(start + 2, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取斜体文本
|
||||
*/
|
||||
private getItalicText(str: string, cursor: number): string | null {
|
||||
// 尝试查找使用 * 的斜体
|
||||
let start = str.lastIndexOf('*', cursor - 1);
|
||||
if (start > 0 && str.charAt(start - 1) === '*') {
|
||||
// 这可能是加粗文本的一部分,跳过
|
||||
start = str.lastIndexOf('*', start - 2);
|
||||
}
|
||||
|
||||
let end = str.indexOf('*', cursor);
|
||||
if (end > 0 && end + 1 < str.length && str.charAt(end + 1) === '*') {
|
||||
// 这可能是加粗文本的一部分,跳过
|
||||
end = str.indexOf('*', end + 2);
|
||||
}
|
||||
|
||||
if (start !== -1 && end !== -1) {
|
||||
return str.substring(start + 1, end);
|
||||
}
|
||||
|
||||
// 尝试查找使用 _ 的斜体
|
||||
start = str.lastIndexOf('_', cursor - 1);
|
||||
end = str.indexOf('_', cursor);
|
||||
|
||||
if (start !== -1 && end !== -1) {
|
||||
return str.substring(start + 1, end);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化国际化设置,使用Obsidian的语言设置
|
||||
*/
|
||||
|
|
@ -371,119 +386,3 @@ export default class EasyCopy extends Plugin {
|
|||
return lang;
|
||||
}
|
||||
}
|
||||
|
||||
class EasyCopySettingTab extends PluginSettingTab {
|
||||
plugin: EasyCopy;
|
||||
|
||||
constructor(app: App, plugin: EasyCopy) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
display(): void {
|
||||
const {containerEl} = this;
|
||||
|
||||
containerEl.empty();
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('add-to-menu'))
|
||||
.setDesc(this.plugin.t('add-to-menu-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.addToMenu)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.addToMenu = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('show-notice'))
|
||||
.setDesc(this.plugin.t('show-notice-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.showNotice)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.showNotice = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
containerEl.createEl('h2', {text: this.plugin.t('format')});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('use-heading-as-display'))
|
||||
.setDesc(this.plugin.t('use-heading-as-display-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.useHeadingAsDisplayText)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.useHeadingAsDisplayText = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('link-format'))
|
||||
.setDesc(this.plugin.t('link-format-desc'))
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption(LinkFormat.MDLINK, this.plugin.t('markdown-link'))
|
||||
.addOption(LinkFormat.WIKILINK, this.plugin.t('wiki-link'))
|
||||
.setValue(this.plugin.settings.linkFormat)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.linkFormat = value as LinkFormat;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
containerEl.createEl('h2', {text: this.plugin.t('target')});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('customize-targets'))
|
||||
.setDesc(this.plugin.t('customize-targets-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.customizeTargets)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.customizeTargets = value;
|
||||
await this.plugin.saveSettings();
|
||||
// 重新渲染设置界面以显示或隐藏目标选项
|
||||
this.display();
|
||||
}));
|
||||
|
||||
// 只有当自定义复制对象选项开启时才显示具体的复制对象选项
|
||||
if (this.plugin.settings.customizeTargets) {
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('enable-inline-code'))
|
||||
.setDesc(this.plugin.t('enable-inline-code-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableInlineCode)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.enableInlineCode = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('enable-bold'))
|
||||
.setDesc(this.plugin.t('enable-bold-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableBold)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.enableBold = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('enable-highlight'))
|
||||
.setDesc(this.plugin.t('enable-highlight-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableHighlight)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.enableHighlight = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('enable-italic'))
|
||||
.setDesc(this.plugin.t('enable-italic-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableItalic)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.enableItalic = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
151
src/settingTab.ts
Normal file
151
src/settingTab.ts
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
import { App, PluginSettingTab, Setting } from "obsidian";
|
||||
import EasyCopy from "./main";
|
||||
import { LinkFormat } from "./type";
|
||||
|
||||
|
||||
export class EasyCopySettingTab extends PluginSettingTab {
|
||||
plugin: EasyCopy;
|
||||
|
||||
constructor(app: App, plugin: EasyCopy) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
display(): void {
|
||||
const {containerEl} = this;
|
||||
|
||||
containerEl.empty();
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('add-to-menu'))
|
||||
.setDesc(this.plugin.t('add-to-menu-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.addToMenu)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.addToMenu = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('show-notice'))
|
||||
.setDesc(this.plugin.t('show-notice-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.showNotice)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.showNotice = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
containerEl.createEl('h2', {text: this.plugin.t('format')});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('use-heading-as-display'))
|
||||
.setDesc(this.plugin.t('use-heading-as-display-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.useHeadingAsDisplayText)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.useHeadingAsDisplayText = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('link-format'))
|
||||
.setDesc(this.plugin.t('link-format-desc'))
|
||||
.addDropdown(dropdown => dropdown
|
||||
.addOption(LinkFormat.MDLINK, this.plugin.t('markdown-link'))
|
||||
.addOption(LinkFormat.WIKILINK, this.plugin.t('wiki-link'))
|
||||
.setValue(this.plugin.settings.linkFormat)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.linkFormat = value as LinkFormat;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
containerEl.createEl('h2', {text: this.plugin.t('target')});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('customize-targets'))
|
||||
.setDesc(this.plugin.t('customize-targets-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.customizeTargets)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.customizeTargets = value;
|
||||
await this.plugin.saveSettings();
|
||||
// 重新渲染设置界面以显示或隐藏目标选项
|
||||
this.display();
|
||||
}));
|
||||
|
||||
// 只有当自定义复制对象选项开启时才显示具体的复制对象选项
|
||||
if (this.plugin.settings.customizeTargets) {
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('enable-inline-code'))
|
||||
.setDesc(this.plugin.t('enable-inline-code-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableInlineCode)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.enableInlineCode = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('enable-bold'))
|
||||
.setDesc(this.plugin.t('enable-bold-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableBold)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.enableBold = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('enable-highlight'))
|
||||
.setDesc(this.plugin.t('enable-highlight-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableHighlight)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.enableHighlight = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('enable-italic'))
|
||||
.setDesc(this.plugin.t('enable-italic-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableItalic)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.enableItalic = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('enable-strikethrough'))
|
||||
.setDesc(this.plugin.t('enable-strikethrough-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableStrikethrough)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.enableStrikethrough = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('enable-inline-latex'))
|
||||
.setDesc(this.plugin.t('enable-inline-latex-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableInlineLatex)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.enableInlineLatex = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName(this.plugin.t('enable-link'))
|
||||
.setDesc(this.plugin.t('enable-link-desc'))
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableLink)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.enableLink = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
55
src/type.ts
Normal file
55
src/type.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
export enum LinkFormat {
|
||||
MDLINK = 'markdown-link',
|
||||
WIKILINK = 'wiki-link'
|
||||
}
|
||||
|
||||
export enum ContextType {
|
||||
NULL = 'null',
|
||||
HEADING = 'heading',
|
||||
INLINECODE = 'inline-code',
|
||||
BOLD = 'bold',
|
||||
ITALIC = 'italic',
|
||||
HIGHLIGHT = 'highlight',
|
||||
STRIKETHROUGH = 'strikethrough',
|
||||
BLOCKID = 'block-id',
|
||||
INLINELATEX = 'inline-latex',
|
||||
LINKTITLE = 'link-title',
|
||||
LINEURL = 'line-url',
|
||||
}
|
||||
|
||||
export interface ContextData {
|
||||
type: ContextType;
|
||||
curLine: string;
|
||||
match: string | null;
|
||||
range: [number, number] | null;
|
||||
}
|
||||
|
||||
export interface EasyCopySettings {
|
||||
addToMenu: boolean;
|
||||
showNotice: boolean;
|
||||
useHeadingAsDisplayText: boolean;
|
||||
linkFormat: LinkFormat;
|
||||
customizeTargets: boolean;
|
||||
enableInlineCode: boolean;
|
||||
enableBold: boolean;
|
||||
enableHighlight: boolean;
|
||||
enableItalic: boolean;
|
||||
enableStrikethrough: boolean;
|
||||
enableInlineLatex: boolean;
|
||||
enableLink: boolean;
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: EasyCopySettings = {
|
||||
addToMenu: true,
|
||||
showNotice: true,
|
||||
useHeadingAsDisplayText: true,
|
||||
linkFormat: LinkFormat.WIKILINK,
|
||||
customizeTargets: false,
|
||||
enableInlineCode: true,
|
||||
enableBold: true,
|
||||
enableHighlight: true,
|
||||
enableItalic: true,
|
||||
enableStrikethrough: true,
|
||||
enableInlineLatex: true,
|
||||
enableLink: true,
|
||||
}
|
||||
Loading…
Reference in a new issue