Repository: platers/obsidian-linter (ts)

Issue:
Bug: No Bare URLs Breakages in Code Block and Smart Typography
## Describe the Bug

The No Bare URLs rule produces a few unexpected results.

1. When using the Smart Typography extension, quoted URLs get converted. E.g.,

    - `‘https://google.com’` → `‘<https://google.com’>`  
    - `“https://google.com”` → `“<https://google.com”>`

2. URLs within backticks containing text other than just the URL. E.g.,

    - `` `http --headers --follow --all https://google.com` ``  → `` `http --headers --follow --all <https://google.com>` ``

![2022-11-02_01](https://user-images.githubusercontent.com/176/199514303-1ea01bb5-d761-4fb1-ac90-a26ec4768c7d.png)

![2022-11-02_02](https://user-images.githubusercontent.com/176/199514349-38a3120e-47eb-4d3c-bdd2-ca686ffe01c9.png)

## How to Reproduce

1. Install Smart Typography extension and use linter on text with curly quotes and No Bare URLs turned on.

2. Has nothing to do with Smart Typography - just run the linter on a backticked string containing a some text + a url.

## Device

- [x] Desktop

Mobile not tested

Pull request description:
No Bare URLs Add Smart Typography as Acceptable Wrapping Characters and Ignore Inline Code
Fixes #469 

Changes Made:
- Refactored the logic for determining if a URL is acceptably escaped to make it simpler visually
- Added UTs to help avoid issues with this down the road
- Updated example to make sure it would pass the test and make it corroborate the functionality of the rule as it was changed

Patch:
diff --git a/src/rules/no-bare-urls.ts b/src/rules/no-bare-urls.ts
index 798a4f7..ca625d4 100644
--- a/src/rules/no-bare-urls.ts
+++ b/src/rules/no-bare-urls.ts
@@ -7,6 +7,8 @@ import {replaceTextBetweenStartAndEndWithNewValue} from '../utils/strings';
 class NoBareUrlsOptions implements Options {
 }
 
+const specialCharsToNotEscapeContentsWithin = `'"‘’“”\`[]`;
+
 @RuleBuilder.register
 export default class NoBareUrls extends RuleBuilder<NoBareUrlsOptions> {
   get OptionsClass(): new () => NoBareUrlsOptions {
@@ -22,8 +24,8 @@ export default class NoBareUrls extends RuleBuilder<NoBareUrlsOptions> {
     return RuleType.CONTENT;
   }
   apply(text: string, options: NoBareUrlsOptions): string {
-    return ignoreListOfTypes([IgnoreTypes.code, IgnoreTypes.yaml, IgnoreTypes.link, IgnoreTypes.wikiLink, IgnoreTypes.tag, IgnoreTypes.image], text, (text) => {
-      const URLMatches = text.match(/(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s`\]'">]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s`\]'">]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s`\]'">]{2,}|www\.[a-zA-Z0-9]+\.[^\s`\]'">]{2,})/gi);
+    return ignoreListOfTypes([IgnoreTypes.code, IgnoreTypes.yaml, IgnoreTypes.link, IgnoreTypes.wikiLink, IgnoreTypes.tag, IgnoreTypes.image, IgnoreTypes.inlineCode], text, (text) => {
+      const URLMatches = text.match(/(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s`\]'"‘’“”>]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s`\]'"‘’“”>]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s`\]'"‘’“”>]{2,}|www\.[a-zA-Z0-9]+\.[^\s`\]'"‘’“”>]{2,})/gi);
 
       if (!URLMatches) {
         return text;
@@ -39,8 +41,8 @@ export default class NoBareUrls extends RuleBuilder<NoBareUrlsOptions> {
 
         const previousChar = urlStart === 0 ? undefined : text.charAt(urlStart - 1);
         const nextChar = urlEnd >= text.length ? undefined : text.charAt(urlEnd);
-        if (previousChar != undefined && (previousChar === '`' || previousChar === '"' || previousChar === '\'' || previousChar === '[') &&
-          nextChar != undefined && (nextChar === '`' || nextChar === '"' || nextChar === '\'' || nextChar === ']')) {
+        if (previousChar != undefined && specialCharsToNotEscapeContentsWithin.includes(previousChar) &&
+          nextChar != undefined && specialCharsToNotEscapeContentsWithin.includes(nextChar)) {
           startSearch = urlStart + urlMatch.length;
           continue;
         }
@@ -97,16 +99,16 @@ export default class NoBareUrls extends RuleBuilder<NoBareUrlsOptions> {
         `,
       }),
       new ExampleBuilder({
-        description: 'Angle brackets are added if the url is not the only text in the single quotes(\'), double quotes("), or backticks(`)',
+        description: 'Angle brackets are added if the url is not the only text in the single quotes(\') or double quotes(")',
         before: dedent`
           [https://github.com some text here]
-          backticks around a url should stay the same, but only if the only contents of the backticks: \`https://github.com some text here\`
+          backticks around a url should stay the same: \`https://github.com some text here\`
           single quotes around a url should stay the same, but only if the contents of the single quotes is the url: 'https://github.com some text here'
           double quotes around a url should stay the same, but only if the contents of the double quotes is the url: "https://github.com some text here"
         `,
         after: dedent`
           [<https://github.com> some text here]
-          backticks around a url should stay the same, but only if the only contents of the backticks: \`<https://github.com> some text here\`
+          backticks around a url should stay the same: \`https://github.com some text here\`
           single quotes around a url should stay the same, but only if the contents of the single quotes is the url: '<https://github.com> some text here'
           double quotes around a url should stay the same, but only if the contents of the double quotes is the url: "<https://github.com> some text here"
         `,


Test patch:
diff --git a/__tests__/no-bare-urls.test.ts b/__tests__/no-bare-urls.test.ts
index 5270180..4c7cab0 100644
--- a/__tests__/no-bare-urls.test.ts
+++ b/__tests__/no-bare-urls.test.ts
@@ -27,5 +27,25 @@ ruleTest({
         <https://google.com#hashtag>
       `,
     },
+    {// accounts for https://github.com/platers/obsidian-linter/issues/469
+      testName: 'Urls that are surrounded by smart quotes should be left alone',
+      before: dedent`
+        “https://google.com”
+        ‘https://google.com’
+      `,
+      after: dedent`
+        “https://google.com”
+        ‘https://google.com’
+      `,
+    },
+    {// accounts for https://github.com/platers/obsidian-linter/issues/469
+      testName: 'Urls that are in inline code should be left alone',
+      before: dedent`
+        \`http --headers --follow --all https://google.com\`
+      `,
+      after: dedent`
+        \`http --headers --follow --all https://google.com\`
+      `,
+    },
   ],
 });