diff --git a/CHANGELOG.md b/CHANGELOG.md index 22e0cf6..18b36c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,5 @@ +### 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 ### 1.0.6 July 22, 2024 diff --git a/README.md b/README.md index 8038523..df58e28 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 `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 | +| ----------- | ----- | +| `{0}` | "target-words-123456" | +| `{1}` | "words" | +| `{2}` | "123456" | +| `{pattern}` | "target-words-123456" | +| `{name}` | "words" | +| `{number}` | "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 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..ad9b15d 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "link-magic", "name": "LinkMagic", - "version": "1.0.8", + "version": "1.1.0", "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..3150373 --- /dev/null +++ b/replacePattern.ts @@ -0,0 +1,48 @@ +/** + * 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); + }, target); +} + +/** + * 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 []; + } + const numberedGroups = Object.entries(matches) + .filter(([key]) => !isNaN(Number(key))); + + const groups = matches.groups || {}; + const namedGroups = Object.entries(groups); + + return [...numberedGroups, ...namedGroups]; +} + +/** + * 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(new RegExp(`\\{${groupName}\\}`, 'g'), 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": [