diff --git a/CHANGELOG.md b/CHANGELOG.md index 7435634..520ac20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +--- + +## [1.7.1] - 2026-05-28 + +### ✨ New Features + +- **Display name regex replacement** (closes [#37](https://github.com/Moyf/easy-copy/issues/37)): added a "Display name: Regex replacement" option under Special copy format options. When enabled, a find-and-replace (with full regex support including capture groups like `$1`) is applied to the display text of copied heading/note links. Useful for stripping leading numbers (e.g. `^\d+\.\s*`) or other prefixes from headings. + +
+中文说明(点击展开) + +### ✨ 新功能 + +- **显示名称正则替换**(修复 [#37](https://github.com/Moyf/easy-copy/issues/37)):在「特殊复制格式选项」中新增「显示名称:正则替换」选项。启用后,可对复制的标题/笔记链接的显示文本进行正则查找替换,支持 `$1` 等捕获组引用。适合去除标题前的序号(如 `^\d+\.\s*`)等场景。 + +
+ --- ## [1.7.0] - 2026-05-26 diff --git a/README.md b/README.md index 439f4e4..307fa5e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Easy Copy - Make Copying Smart and Simple! -English | [中文文档](./README-zh.md) +English | [中文文档](https://github.com/Moyf/easy-copy/blob/master/README-zh.md) ![Obsidian Download](https://img.shields.io/badge/dynamic/json?logo=obsidian&color=%23483699&label=Downloads&query=%24%5B%22easy-copy%22%5D.downloads&url=https%3A%2F%2Fraw.githubusercontent.com%2Fobsidianmd%2Fobsidian-releases%2Fmaster%2Fcommunity-plugin-stats.json) ![Total Downloads](https://img.shields.io/github/downloads/Moyf/easy-copy/total?style=flat&label=Total%20Downloads) ![GitHub Issues](https://img.shields.io/github/issues/Moyf/easy-copy?style=flat&label=Issues) ![GitHub Last Commit](https://img.shields.io/github/last-commit/Moyf/easy-copy?style=flat&label=Last%20Commit) diff --git a/manifest.json b/manifest.json index 9d76a54..844395a 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "easy-copy", "name": "Easy Copy", - "version": "1.7.0", + "version": "1.7.1", "minAppVersion": "1.8.7", "description": "Easily copy the text within inline code, bold text (and many other formats), or quickly generate an elegant link to a heading.", "author": "Moy", diff --git a/package.json b/package.json index 63b2047..f0e71c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "easy-copy", - "version": "1.7.0", + "version": "1.7.1", "description": "This is a sample plugin for Obsidian (https://obsidian.md)", "main": "main.js", "scripts": { diff --git a/src/i18n.ts b/src/i18n.ts index ec1b63c..d6208a3 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -46,7 +46,10 @@ export type TranslationKey = | 'block-display-word-limit' | 'block-display-word-limit-desc' | 'block-display-char-limit' | 'block-display-char-limit-desc' | 'generate-current-block-link-auto' | 'generate-current-block-link-manual' - | 'error-block-id-empty' | 'error-block-id-invalid'; + | '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'; // 本地化翻译字典 export const translations: Record> = { @@ -163,6 +166,12 @@ export const translations: Record> = { 'generate-current-block-link-auto': 'Copy current block link (auto-generate ID)', 'generate-current-block-link-manual': 'Copy current block link (manual ID input)', 'file-link-copied': 'File link copied!', + 'enable-display-name-regex': 'Display name: Regex replacement', + 'enable-display-name-regex-desc': 'When enabled, apply a regex find-and-replace to the display text of copied heading/note links', + 'display-name-regex-from': 'Find (regex)', + '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)', }, [Language.ZH]: { // 复制 Block ID @@ -275,6 +284,12 @@ export const translations: Record> = { 'block-display-word-limit-desc': '使用空格分隔的语言(如英语 "this is a sentence")在块显示文本中显示的最大单词数', 'block-display-char-limit': '块显示文本:CJK 类语言的字符数限制', 'block-display-char-limit-desc': '非英语类语言(如中文 "这是一句话")在块显示文本中显示的最大字符数——当第一行包含非ASCII字符时,会采用此设置。', + 'enable-display-name-regex': '显示名称:正则替换', + 'enable-display-name-regex-desc': '启用后,对复制的标题/笔记链接的显示文本进行正则查找替换', + 'display-name-regex-from': '查找(正则)', + 'display-name-regex-from-desc': '用于匹配显示文本的正则表达式(如 ^\\d+\\.\\s* 可去除开头的序号)', + 'display-name-regex-to': '替换为', + 'display-name-regex-to-desc': '替换字符串,支持捕获组引用如 $1、$2(如 $1 只保留第一个捕获组的内容)', }, [Language.ZH_TW]: { // 复制 Block ID @@ -388,6 +403,12 @@ export const translations: Record> = { 'use-frontmatter-as-display-desc': '啟用後,使用指定的筆記屬性的值作為筆記連結的顯示文本', 'frontmatter-key': '筆記屬性名', 'frontmatter-key-desc': '用於顯示文本的筆記屬性名(默認:title)', + 'enable-display-name-regex': '顯示名稱:正規表示式替換', + 'enable-display-name-regex-desc': '啟用後,對複製的標題/筆記連結的顯示文本進行正規表示式查找替換', + 'display-name-regex-from': '查找(正則)', + 'display-name-regex-from-desc': '用於匹配顯示文本的正規表示式(如 ^\\d+\\.\\s* 可去除開頭的序號)', + 'display-name-regex-to': '替換為', + 'display-name-regex-to-desc': '替換字符串,支持捕獲組引用如 $1、$2', } }; diff --git a/src/linkBuilder.ts b/src/linkBuilder.ts index 78ba3e8..2f165af 100644 --- a/src/linkBuilder.ts +++ b/src/linkBuilder.ts @@ -47,6 +47,9 @@ export interface BuildHeadingLinkOptions { headingLinkSeparator: string; strictHeadingMatch?: boolean; simplifiedHeadingToNoteLink: boolean; + enableDisplayNameRegex?: boolean; + displayNameRegexFrom?: string; + displayNameRegexTo?: string; } export interface BuildHeadingLinkResult { @@ -127,7 +130,7 @@ function formatMarkdownHeadingLink(o: FormatMarkdownHeadingLinkOptions): string export function buildHeadingLink(options: BuildHeadingLinkOptions): BuildHeadingLinkResult { const selectedHeading = stripWikiBrackets(options.heading); - const displayText = computeDisplayText({ + let displayText = computeDisplayText({ heading: selectedHeading, filename: options.filename, frontmatterTitle: options.frontmatterTitle, @@ -135,6 +138,17 @@ export function buildHeadingLink(options: BuildHeadingLinkOptions): BuildHeading headingLinkSeparator: options.headingLinkSeparator, }); + // 应用正则替换显示名称 + if (options.enableDisplayNameRegex && options.displayNameRegexFrom) { + try { + const regex = new RegExp(options.displayNameRegexFrom, 'gm'); + const replaceTo = options.displayNameRegexTo ?? ''; + displayText = displayText.replace(regex, replaceTo); + } catch (e) { + console.warn('[Easy Copy] Invalid display name regex:', e); + } + } + const simplify = shouldSimplifyHeading({ simplifiedHeadingToNoteLink: options.simplifiedHeadingToNoteLink, useHeadingAsDisplayText: options.useHeadingAsDisplayText, diff --git a/src/main.ts b/src/main.ts index d56c67c..bcff772 100644 --- a/src/main.ts +++ b/src/main.ts @@ -760,6 +760,9 @@ export default class EasyCopy extends Plugin { headingLinkSeparator: this.settings.headingLinkSeparator, strictHeadingMatch: this.settings.strictHeadingMatch, simplifiedHeadingToNoteLink: this.settings.simplifiedHeadingToNoteLink, + enableDisplayNameRegex: this.settings.enableDisplayNameRegex, + displayNameRegexFrom: this.settings.displayNameRegexFrom, + displayNameRegexTo: this.settings.displayNameRegexTo, }); void navigator.clipboard.writeText(link); diff --git a/src/settingTab.ts b/src/settingTab.ts index ad9af4f..d0535c3 100644 --- a/src/settingTab.ts +++ b/src/settingTab.ts @@ -477,5 +477,41 @@ export class EasyCopySettingTab extends PluginSettingTab { void this.plugin.saveSettings(); }))); } + + // 正则替换显示名称 + specialFormatGroup.addSetting(setting => setting + .setName(this.plugin.t('enable-display-name-regex')) + .setDesc(this.plugin.t('enable-display-name-regex-desc')) + .addToggle(toggle => toggle + .setValue(this.plugin.settings.enableDisplayNameRegex ?? false) + .onChange( value => { + this.plugin.settings.enableDisplayNameRegex = value; + void this.plugin.saveSettings(); + this.display(); + }))); + + if (this.plugin.settings.enableDisplayNameRegex) { + specialFormatGroup.addSetting(setting => setting + .setName(this.plugin.t('display-name-regex-from')) + .setDesc(this.plugin.t('display-name-regex-from-desc')) + .addText(text => text + .setPlaceholder('e.g. ^\\d+\\.\\s*') + .setValue(this.plugin.settings.displayNameRegexFrom ?? '') + .onChange( value => { + this.plugin.settings.displayNameRegexFrom = value; + void this.plugin.saveSettings(); + }))); + + specialFormatGroup.addSetting(setting => setting + .setName(this.plugin.t('display-name-regex-to')) + .setDesc(this.plugin.t('display-name-regex-to-desc')) + .addText(text => text + .setPlaceholder('e.g. $1') + .setValue(this.plugin.settings.displayNameRegexTo ?? '') + .onChange( value => { + this.plugin.settings.displayNameRegexTo = value; + void this.plugin.saveSettings(); + }))); + } } } diff --git a/src/type.ts b/src/type.ts index c43c634..404b868 100644 --- a/src/type.ts +++ b/src/type.ts @@ -73,6 +73,9 @@ export interface EasyCopySettings { autoBlockDisplayText: boolean; // 自动为 Block 添加显示文本 blockDisplayWordLimit: number; // Block 显示文本英文单词限制(按空格分隔) blockDisplayCharLimit: number; // Block 显示文本字符限制(非英文语言) + enableDisplayNameRegex: boolean; // 是否启用正则替换显示名称 + displayNameRegexFrom: string; // 正则替换:from 模式 + displayNameRegexTo: string; // 正则替换:to 内容(支持 $1 等捕获组) } export const DEFAULT_SETTINGS: EasyCopySettings = { @@ -107,4 +110,7 @@ export const DEFAULT_SETTINGS: EasyCopySettings = { autoBlockDisplayText: true, blockDisplayWordLimit: 3, // 英文单词限制:3个单词 blockDisplayCharLimit: 5, // 字符限制:5个字符 + enableDisplayNameRegex: false, + displayNameRegexFrom: '', + displayNameRegexTo: '', } \ No newline at end of file diff --git a/versions.json b/versions.json index d1fe0a0..09f85fc 100644 --- a/versions.json +++ b/versions.json @@ -21,5 +21,6 @@ "1.6.3": "0.15.0", "1.6.4": "1.8.7", "1.6.5": "1.8.7", - "1.7.0": "1.8.7" + "1.7.0": "1.8.7", + "1.7.1": "1.8.7" }