mirror of
https://github.com/moyf/easy-copy.git
synced 2026-07-22 05:43:47 +00:00
feat: allow built-in matcher reordering
This commit is contained in:
parent
0bfb72a41d
commit
3e72472a4d
5 changed files with 120 additions and 16 deletions
|
|
@ -1,13 +1,14 @@
|
|||
import { ContextType, EasyCopySettings } from './type';
|
||||
import { BuiltinCopyMatcherId, ContextType, EasyCopySettings } from './type';
|
||||
|
||||
export type BuiltinCopyMatcherId =
|
||||
| 'bold'
|
||||
| 'italic'
|
||||
| 'highlight'
|
||||
| 'strikethrough'
|
||||
| 'inline-code'
|
||||
| 'inline-latex'
|
||||
| 'wiki-link';
|
||||
export const DEFAULT_BUILTIN_COPY_MATCHER_IDS: BuiltinCopyMatcherId[] = [
|
||||
'bold',
|
||||
'italic',
|
||||
'highlight',
|
||||
'strikethrough',
|
||||
'inline-code',
|
||||
'inline-latex',
|
||||
'wiki-link',
|
||||
];
|
||||
|
||||
export interface CopyMatcher {
|
||||
id: BuiltinCopyMatcherId;
|
||||
|
|
@ -16,6 +17,23 @@ export interface CopyMatcher {
|
|||
enabled: boolean;
|
||||
}
|
||||
|
||||
export function normalizeBuiltinMatcherOrder(order: readonly string[] | undefined): BuiltinCopyMatcherId[] {
|
||||
const knownIds = new Set<string>(DEFAULT_BUILTIN_COPY_MATCHER_IDS);
|
||||
const orderedIds: BuiltinCopyMatcherId[] = [];
|
||||
|
||||
for (const id of order ?? []) {
|
||||
if (knownIds.has(id) && !orderedIds.includes(id as BuiltinCopyMatcherId)) {
|
||||
orderedIds.push(id as BuiltinCopyMatcherId);
|
||||
}
|
||||
}
|
||||
|
||||
for (const id of DEFAULT_BUILTIN_COPY_MATCHER_IDS) {
|
||||
if (!orderedIds.includes(id)) orderedIds.push(id);
|
||||
}
|
||||
|
||||
return orderedIds;
|
||||
}
|
||||
|
||||
export function buildBuiltinCopyMatchers(settings: EasyCopySettings, isIosApp: boolean): CopyMatcher[] {
|
||||
// iOS 16.4 之前不支持后视(Lookbehinds),但支持前视(Lookaheads)
|
||||
// 所以针对 iOS 平台使用只带前视的正则表达式,其他平台使用完整版本
|
||||
|
|
@ -25,7 +43,7 @@ export function buildBuiltinCopyMatchers(settings: EasyCopySettings, isIosApp: b
|
|||
|
||||
const boldRegex = /(?:\*\*([^*]+)\*\*|__([^_]+)__)/g;
|
||||
|
||||
return [
|
||||
const matchers: CopyMatcher[] = [
|
||||
{ id: 'bold', type: ContextType.BOLD, regex: boldRegex, enabled: !settings.customizeTargets || settings.enableBold },
|
||||
{ id: 'italic', type: ContextType.ITALIC, regex: italicRegex, enabled: !settings.customizeTargets || settings.enableItalic },
|
||||
{ id: 'highlight', type: ContextType.HIGHLIGHT, regex: /==([^=]+)==/g, enabled: !settings.customizeTargets || settings.enableHighlight },
|
||||
|
|
@ -34,4 +52,7 @@ export function buildBuiltinCopyMatchers(settings: EasyCopySettings, isIosApp: b
|
|||
{ id: 'inline-latex', type: ContextType.INLINELATEX, regex: /\$([^$]+)\$/g, enabled: !settings.customizeTargets || settings.enableInlineLatex },
|
||||
{ id: 'wiki-link', type: ContextType.WIKILINK, regex: /\[\[([^\]]+)\]\]/g, enabled: !settings.customizeTargets || settings.enableWikiLink },
|
||||
];
|
||||
|
||||
const matcherById = new Map(matchers.map(matcher => [matcher.id, matcher]));
|
||||
return normalizeBuiltinMatcherOrder(settings.matcherOrder).map(id => matcherById.get(id)!);
|
||||
}
|
||||
|
|
|
|||
18
src/i18n.ts
18
src/i18n.ts
|
|
@ -49,7 +49,8 @@ export type TranslationKey =
|
|||
| 'error-block-id-empty' | 'error-block-id-invalid'
|
||||
| 'enable-display-name-regex' | 'enable-display-name-regex-desc'
|
||||
| 'display-name-regex-from' | 'display-name-regex-from-desc'
|
||||
| 'display-name-regex-to' | 'display-name-regex-to-desc';
|
||||
| 'display-name-regex-to' | 'display-name-regex-to-desc'
|
||||
| 'matcher-priority' | 'matcher-priority-desc' | 'move-up' | 'move-down' | 'reset-order';
|
||||
|
||||
// 本地化翻译字典
|
||||
export const translations: Record<Language, Record<TranslationKey, string>> = {
|
||||
|
|
@ -172,6 +173,11 @@ export const translations: Record<Language, Record<TranslationKey, string>> = {
|
|||
'display-name-regex-from-desc': 'Regular expression pattern to match in the display text (e.g. ^\\d+\\.\\s* to remove leading numbers)',
|
||||
'display-name-regex-to': 'Replace with',
|
||||
'display-name-regex-to-desc': 'Replacement string, supports capture groups like $1, $2 (e.g. $1 to keep only the first captured group)',
|
||||
'matcher-priority': 'Copy target priority',
|
||||
'matcher-priority-desc': 'When multiple targets match the cursor, targets higher in this list are copied first.',
|
||||
'move-up': 'Move up',
|
||||
'move-down': 'Move down',
|
||||
'reset-order': 'Reset order',
|
||||
},
|
||||
[Language.ZH]: {
|
||||
// 复制 Block ID
|
||||
|
|
@ -290,6 +296,11 @@ export const translations: Record<Language, Record<TranslationKey, string>> = {
|
|||
'display-name-regex-from-desc': '用于匹配显示文本的正则表达式(如 ^\\d+\\.\\s* 可去除开头的序号)',
|
||||
'display-name-regex-to': '替换为',
|
||||
'display-name-regex-to-desc': '替换字符串,支持捕获组引用如 $1、$2(如 $1 只保留第一个捕获组的内容)',
|
||||
'matcher-priority': '复制对象优先级',
|
||||
'matcher-priority-desc': '当多种复制对象同时匹配光标位置时,列表中更靠上的对象会优先生效。',
|
||||
'move-up': '上移',
|
||||
'move-down': '下移',
|
||||
'reset-order': '重置顺序',
|
||||
},
|
||||
[Language.ZH_TW]: {
|
||||
// 复制 Block ID
|
||||
|
|
@ -409,6 +420,11 @@ export const translations: Record<Language, Record<TranslationKey, string>> = {
|
|||
'display-name-regex-from-desc': '用於匹配顯示文本的正規表示式(如 ^\\d+\\.\\s* 可去除開頭的序號)',
|
||||
'display-name-regex-to': '替換為',
|
||||
'display-name-regex-to-desc': '替換字符串,支持捕獲組引用如 $1、$2',
|
||||
'matcher-priority': '複製對象優先級',
|
||||
'matcher-priority-desc': '當多種複製對象同時匹配游標位置時,列表中更靠上的對象會優先生效。',
|
||||
'move-up': '上移',
|
||||
'move-down': '下移',
|
||||
'reset-order': '重置順序',
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { ContextData, ContextType, DEFAULT_SETTINGS, EasyCopySettings, LinkForma
|
|||
import { EasyCopySettingTab } from './settingTab';
|
||||
import { BlockIdInputModal } from './blockIdModal';
|
||||
import { detectCodeBlockFromLines } from './codeBlockDetect';
|
||||
import { buildBuiltinCopyMatchers } from './copyMatcher';
|
||||
import { buildBuiltinCopyMatchers, normalizeBuiltinMatcherOrder } from './copyMatcher';
|
||||
import { buildHeadingLink, buildBlockLink, buildFileLink, buildExplicitPasteLink } from './linkBuilder';
|
||||
import { CopyMetadata, buildBlockCopyMetadata, buildHeadingCopyMetadata, buildFileCopyMetadata } from './copyMetadata';
|
||||
import { decidePasteResolution, shouldOmitAliasForSameFile, shouldRegisterPasteHandler } from './pasteResolution';
|
||||
|
|
@ -161,6 +161,7 @@ export default class EasyCopy extends Plugin {
|
|||
|
||||
async loadSettings() {
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
||||
this.settings.matcherOrder = normalizeBuiltinMatcherOrder(this.settings.matcherOrder);
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
|
|
|
|||
|
|
@ -7,12 +7,23 @@ import {
|
|||
} from "obsidian";
|
||||
import * as ObsidianModule from "obsidian";
|
||||
import EasyCopy from "./main";
|
||||
import { LinkFormat, BlockIdInsertPosition, CodeBlockBehavior } from "./type";
|
||||
import { BuiltinCopyMatcherId, LinkFormat, BlockIdInsertPosition, CodeBlockBehavior } from "./type";
|
||||
import { DEFAULT_BUILTIN_COPY_MATCHER_IDS, normalizeBuiltinMatcherOrder } from "./copyMatcher";
|
||||
|
||||
interface SettingsContainer {
|
||||
addSetting(cb: (setting: Setting) => void): void;
|
||||
}
|
||||
|
||||
const BUILTIN_MATCHER_LABEL_KEYS: Record<BuiltinCopyMatcherId, 'enable-bold' | 'enable-italic' | 'enable-highlight' | 'enable-strikethrough' | 'enable-inline-code' | 'enable-inline-latex' | 'enable-wikilink'> = {
|
||||
'bold': 'enable-bold',
|
||||
'italic': 'enable-italic',
|
||||
'highlight': 'enable-highlight',
|
||||
'strikethrough': 'enable-strikethrough',
|
||||
'inline-code': 'enable-inline-code',
|
||||
'inline-latex': 'enable-inline-latex',
|
||||
'wiki-link': 'enable-wikilink',
|
||||
};
|
||||
|
||||
function createSettingsGroup(containerEl: HTMLElement, heading?: string): SettingsContainer {
|
||||
// Check if SettingGroup is available (API 1.11.0+)
|
||||
// requireApiVersion is the official Obsidian API method for version checking
|
||||
|
|
@ -345,6 +356,8 @@ export class EasyCopySettingTab extends PluginSettingTab {
|
|||
|
||||
// 只有当自定义复制对象选项开启时才显示具体的复制对象选项
|
||||
if (this.plugin.settings.customizeTargets) {
|
||||
this.plugin.settings.matcherOrder = normalizeBuiltinMatcherOrder(this.plugin.settings.matcherOrder);
|
||||
|
||||
targetGroup.addSetting(setting => setting
|
||||
.setName(this.plugin.t('enable-inline-code'))
|
||||
.setDesc(this.plugin.t('enable-inline-code-desc'))
|
||||
|
|
@ -424,8 +437,38 @@ export class EasyCopySettingTab extends PluginSettingTab {
|
|||
this.plugin.settings.enableWikiLink = value;
|
||||
void this.plugin.saveSettings();
|
||||
this.display(); // 切换后刷新界面以显示/隐藏下方选项
|
||||
})));
|
||||
|
||||
})));
|
||||
|
||||
targetGroup.addSetting(setting => setting
|
||||
.setName(this.plugin.t('matcher-priority'))
|
||||
.setDesc(this.plugin.t('matcher-priority-desc'))
|
||||
.addButton(button => button
|
||||
.setButtonText(this.plugin.t('reset-order'))
|
||||
.onClick(() => {
|
||||
this.plugin.settings.matcherOrder = [...DEFAULT_BUILTIN_COPY_MATCHER_IDS];
|
||||
void this.plugin.saveSettings();
|
||||
this.display();
|
||||
})));
|
||||
|
||||
this.plugin.settings.matcherOrder.forEach((matcherId, index) => {
|
||||
targetGroup.addSetting(setting => setting
|
||||
.setName(this.plugin.t(BUILTIN_MATCHER_LABEL_KEYS[matcherId]))
|
||||
.addButton(button => button
|
||||
.setButtonText('↑')
|
||||
.setTooltip(this.plugin.t('move-up'))
|
||||
.setDisabled(index === 0)
|
||||
.onClick(() => {
|
||||
this.moveMatcher(matcherId, -1);
|
||||
}))
|
||||
.addButton(button => button
|
||||
.setButtonText('↓')
|
||||
.setTooltip(this.plugin.t('move-down'))
|
||||
.setDisabled(index === this.plugin.settings.matcherOrder.length - 1)
|
||||
.onClick(() => {
|
||||
this.moveMatcher(matcherId, 1);
|
||||
})));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
targetGroup.addSetting(setting => setting
|
||||
|
|
@ -514,4 +557,16 @@ export class EasyCopySettingTab extends PluginSettingTab {
|
|||
})));
|
||||
}
|
||||
}
|
||||
|
||||
private moveMatcher(matcherId: BuiltinCopyMatcherId, direction: -1 | 1): void {
|
||||
const matcherOrder = normalizeBuiltinMatcherOrder(this.plugin.settings.matcherOrder);
|
||||
const currentIndex = matcherOrder.indexOf(matcherId);
|
||||
const nextIndex = currentIndex + direction;
|
||||
if (nextIndex < 0 || nextIndex >= matcherOrder.length) return;
|
||||
|
||||
[matcherOrder[currentIndex], matcherOrder[nextIndex]] = [matcherOrder[nextIndex], matcherOrder[currentIndex]];
|
||||
this.plugin.settings.matcherOrder = matcherOrder;
|
||||
void this.plugin.saveSettings();
|
||||
this.display();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
13
src/type.ts
13
src/type.ts
|
|
@ -34,6 +34,15 @@ export enum ContextType {
|
|||
CODEBLOCK = 'code-block', // 光标在代码块内
|
||||
}
|
||||
|
||||
export type BuiltinCopyMatcherId =
|
||||
| 'bold'
|
||||
| 'italic'
|
||||
| 'highlight'
|
||||
| 'strikethrough'
|
||||
| 'inline-code'
|
||||
| 'inline-latex'
|
||||
| 'wiki-link';
|
||||
|
||||
export interface ContextData {
|
||||
type: ContextType;
|
||||
curLine: string;
|
||||
|
|
@ -62,6 +71,7 @@ export interface EasyCopySettings {
|
|||
enableInlineLatex: boolean;
|
||||
enableLink: boolean;
|
||||
enableWikiLink: boolean; // 是否启用 Wiki 链接复制
|
||||
matcherOrder: BuiltinCopyMatcherId[]; // 复制对象匹配优先级
|
||||
keepWikiBrackets: boolean; // 复制 wiki-link 时保留 [[ ]]
|
||||
autoEmbedBlockLink: boolean; // 复制块链接时自动添加 !(嵌入块)
|
||||
enableCalloutCopy: boolean; // 是否启用复制 Callout 内文本
|
||||
|
|
@ -99,6 +109,7 @@ export const DEFAULT_SETTINGS: EasyCopySettings = {
|
|||
enableInlineLatex: true,
|
||||
enableLink: true,
|
||||
enableWikiLink: true,
|
||||
matcherOrder: ['bold', 'italic', 'highlight', 'strikethrough', 'inline-code', 'inline-latex', 'wiki-link'],
|
||||
keepWikiBrackets: true,
|
||||
autoEmbedBlockLink: false,
|
||||
enableCalloutCopy: true,
|
||||
|
|
@ -113,4 +124,4 @@ export const DEFAULT_SETTINGS: EasyCopySettings = {
|
|||
enableDisplayNameRegex: false,
|
||||
displayNameRegexFrom: '',
|
||||
displayNameRegexTo: '',
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue