Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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-(?<name>[a-zA-Z]+)-(?<number>[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

Expand Down
3 changes: 2 additions & 1 deletion main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { App, Editor, EditorPosition, Plugin, PluginSettingTab, Setting } from 'obsidian';
import { replacePattern } from 'replacePattern';


type PluginSettings = Record<string, { pattern: string, link: string }>;
Expand Down Expand Up @@ -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);
}
}
Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
48 changes: 48 additions & 0 deletions replacePattern.ts
Original file line number Diff line number Diff line change
@@ -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);
}
5 changes: 3 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
"moduleResolution": "node",
"importHelpers": true,
"isolatedModules": true,
"strictNullChecks": true,
"strictNullChecks": true,
"lib": [
"DOM",
"ES5",
"ES6",
"ES7"
"ES7",
"ES2018"
]
},
"include": [
Expand Down