From 2e71fb325fdb6eda82f76505c307cfa491d7846a Mon Sep 17 00:00:00 2001 From: Orjan De Smet Date: Wed, 20 Aug 2025 16:27:28 +0200 Subject: [PATCH 1/6] Support RegExp groups --- CHANGELOG.md | 2 ++ README.md | 15 +++++++++++++++ main.ts | 3 ++- manifest.json | 2 +- replacePattern.ts | 27 +++++++++++++++++++++++++++ tsconfig.json | 5 +++-- 6 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 replacePattern.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 22e0cf6..c48f045 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +### 1.0.9 Aug 20, 2025 + - Allow Regex groups to be used in replacement ### 1.0.8 Aug 28, 2024 - Update README.md with a more verbose example ### 1.0.6 July 22, 2024 diff --git a/README.md b/README.md index 8038523..78ceee4 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,21 @@ To create a rule that automatically converts Jira issue keys into clickable link ![An image showing the settings page for LinkMagic with an example pattern](example.png) +## Using RegExp groups + +RegExp groups are completely supported, by using the group number or name in the replacement link. +For example, in a string `There is some target-words-123456 in a sentence` the pattern `target-(?[a-zA-Z]+)-(?[0-9]+)` allows the following replacements: + +| replacement | value | +| ----------- | ----- | +| `{0}` | "target-words-123456" | +| `{1}` | "words" | +| `{2}` | "123456" | +| `{pattern}` | "target-words-123456" | +| `{name}` | "words" | +| `{number}` | "123456" | + +A link `https://example.com/{name}/{number}` would then become `https://example.com/words/123456` # Support and Feedback diff --git a/main.ts b/main.ts index f0926c8..4c7e81f 100644 --- a/main.ts +++ b/main.ts @@ -1,4 +1,5 @@ import { App, Editor, EditorPosition, Plugin, PluginSettingTab, Setting } from 'obsidian'; +import { replacePattern } from 'replacePattern'; type PluginSettings = Record; @@ -68,7 +69,7 @@ export default class LinkMagicPlugin extends Plugin { const startChar = match[1] || "" const innerWord = match[2] const endChar = match[3] || "" - let markdownLink = `${startChar}[${innerWord}](${link.replace("{pattern}", innerWord)})${endChar}` + let markdownLink = `${startChar}[${innerWord}](${replacePattern(link, regex, innerWord)})${endChar}` editor.replaceRange(markdownLink, { line: curPos.line, ch: curPos.ch - word.length }, curPos); } } diff --git a/manifest.json b/manifest.json index 72258a5..ff97ce7 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "link-magic", "name": "LinkMagic", - "version": "1.0.8", + "version": "1.0.9", "minAppVersion": "0.15.0", "description": "Automatically adds links to defined regex.", "author": "Andrew Reifman", diff --git a/replacePattern.ts b/replacePattern.ts new file mode 100644 index 0000000..4f0303f --- /dev/null +++ b/replacePattern.ts @@ -0,0 +1,27 @@ +export function replacePattern(link: string, pattern: string, innerWord: string) { + const groups = findGroups(innerWord, pattern); + + const replacements = [["pattern", innerWord], ...groups]; + + return replacements.reduce((acc, [groupName, replacement]) => { + return replaceGroup(acc, groupName, replacement); + }, link); +} + +function findGroups(innerWord: string, pattern: string): string[][] { + const matches = new RegExp(pattern, "g").exec(innerWord); + if (!matches) { + return []; + } + const numberedGroups = Object.entries(matches) + .filter(([key]) => !isNaN(Number(key))); + + const groups = matches.groups || {}; + const namedGroups = Object.entries(groups); + + return [...numberedGroups, ...namedGroups]; +} + +function replaceGroup(input: string, groupName: string, replacement: string): string { + return input.replace(`{${groupName}}`, replacement); +} diff --git a/tsconfig.json b/tsconfig.json index 2d6fbdf..7b55e9f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,12 +10,13 @@ "moduleResolution": "node", "importHelpers": true, "isolatedModules": true, - "strictNullChecks": true, + "strictNullChecks": true, "lib": [ "DOM", "ES5", "ES6", - "ES7" + "ES7", + "ES2018" ] }, "include": [ From c64614c51b4ffe10204d60ae9bcfb6879ec55691 Mon Sep 17 00:00:00 2001 From: Orjan De Smet Date: Wed, 3 Sep 2025 10:13:41 +0200 Subject: [PATCH 2/6] Code Review Remark - Correct types of pattern parameter --- replacePattern.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/replacePattern.ts b/replacePattern.ts index 4f0303f..01f74d8 100644 --- a/replacePattern.ts +++ b/replacePattern.ts @@ -1,4 +1,4 @@ -export function replacePattern(link: string, pattern: string, innerWord: string) { +export function replacePattern(link: string, pattern: RegExp, innerWord: string) { const groups = findGroups(innerWord, pattern); const replacements = [["pattern", innerWord], ...groups]; @@ -8,8 +8,8 @@ export function replacePattern(link: string, pattern: string, innerWord: string) }, link); } -function findGroups(innerWord: string, pattern: string): string[][] { - const matches = new RegExp(pattern, "g").exec(innerWord); +function findGroups(innerWord: string, pattern: RegExp): string[][] { + const matches = pattern.exec(innerWord); if (!matches) { return []; } From 2a74af08e98caaacdae02f0b442d92fc68db337d Mon Sep 17 00:00:00 2001 From: Orjan De Smet Date: Wed, 3 Sep 2025 10:14:54 +0200 Subject: [PATCH 3/6] Code Review Remark - Add docstrings --- replacePattern.ts | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/replacePattern.ts b/replacePattern.ts index 01f74d8..7c054cd 100644 --- a/replacePattern.ts +++ b/replacePattern.ts @@ -1,15 +1,29 @@ -export function replacePattern(link: string, pattern: RegExp, innerWord: string) { +/** + * Finds all RegExp groups in the innerWord and uses them to replace matching group in the target string with their replacement from the inner word. + * The group name {pattern} is kept for backwards compatibility. This group gets replaced with the full innerWord. + * @param target The target string to do all replacements in + * @param pattern + * @param innerWord The keyword from the source text that triggered this replacement + * @returns a string based on the target string, in which all occurences of group names or numbers are replaced with their matching group in the innerWord + */ +export function replacePattern(target: string, pattern: RegExp, innerWord: string) { const groups = findGroups(innerWord, pattern); const replacements = [["pattern", innerWord], ...groups]; return replacements.reduce((acc, [groupName, replacement]) => { return replaceGroup(acc, groupName, replacement); - }, link); + }, target); } -function findGroups(innerWord: string, pattern: RegExp): string[][] { - const matches = pattern.exec(innerWord); +/** + * Lists matching RegExp groups in the input string. This includes numbered and named groups. + * @param input + * @param pattern + * @returns An array of [groupName, replacement] tuples + */ +function findGroups(input: string, pattern: RegExp): string[][] { + const matches = pattern.exec(input); if (!matches) { return []; } @@ -22,6 +36,13 @@ function findGroups(innerWord: string, pattern: RegExp): string[][] { return [...numberedGroups, ...namedGroups]; } +/** + * Replaces a substring {groupName} in the input string with the replacement + * @param input + * @param groupName + * @param replacement + * @returns The string with the replaced values + */ function replaceGroup(input: string, groupName: string, replacement: string): string { return input.replace(`{${groupName}}`, replacement); } From b87c3bebb9034a2c4d5fb2c13d827517db471db6 Mon Sep 17 00:00:00 2001 From: Orjan De Smet Date: Wed, 3 Sep 2025 10:15:20 +0200 Subject: [PATCH 4/6] Replace all occurences of a groupName instead of only the first one --- replacePattern.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/replacePattern.ts b/replacePattern.ts index 7c054cd..3150373 100644 --- a/replacePattern.ts +++ b/replacePattern.ts @@ -37,12 +37,12 @@ function findGroups(input: string, pattern: RegExp): string[][] { } /** - * Replaces a substring {groupName} in the input string with the replacement + * Replaces all substrings {groupName} in the input string with the replacement * @param input * @param groupName * @param replacement * @returns The string with the replaced values */ function replaceGroup(input: string, groupName: string, replacement: string): string { - return input.replace(`{${groupName}}`, replacement); + return input.replace(new RegExp(`\\{${groupName}\\}`, 'g'), replacement); } From 6bf053a0ec9baac6bdefd8c78060d7a02e0d6a24 Mon Sep 17 00:00:00 2001 From: Orjan De Smet Date: Wed, 3 Sep 2025 10:15:42 +0200 Subject: [PATCH 5/6] Update version info --- CHANGELOG.md | 2 +- manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c48f045..18b36c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### 1.0.9 Aug 20, 2025 +### 1.1.0 Sep 3, 2025 - Allow Regex groups to be used in replacement ### 1.0.8 Aug 28, 2024 - Update README.md with a more verbose example diff --git a/manifest.json b/manifest.json index ff97ce7..ad9b15d 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "link-magic", "name": "LinkMagic", - "version": "1.0.9", + "version": "1.1.0", "minAppVersion": "0.15.0", "description": "Automatically adds links to defined regex.", "author": "Andrew Reifman", From 4577458ecd034400bfed009d538c5f966886dfc9 Mon Sep 17 00:00:00 2001 From: Orjan De Smet Date: Wed, 3 Sep 2025 10:20:07 +0200 Subject: [PATCH 6/6] Update example in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 78ceee4..df58e28 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ To create a rule that automatically converts Jira issue keys into clickable link ## Using RegExp groups RegExp groups are completely supported, by using the group number or name in the replacement link. -For example, in a string `There is some target-words-123456 in a sentence` the pattern `target-(?[a-zA-Z]+)-(?[0-9]+)` allows the following replacements: +For example, in a string `This is an example sentence where target-words-123456 will be replaced.` the pattern `target-(?[a-zA-Z]+)-(?[0-9]+)` allows the following replacements: | replacement | value | | ----------- | ----- | @@ -41,7 +41,7 @@ For example, in a string `There is some target-words-123456 in a sentence` the p | `{name}` | "words" | | `{number}` | "123456" | -A link `https://example.com/{name}/{number}` would then become `https://example.com/words/123456` +With the above pattern and a target link `https://example.com/{name}/{number}`, the example string from above would then become `This is an example sentence where [target-words-123456](https://example.com/words/123456) will be replaced.`. # Support and Feedback