Repository: platers/obsidian-linter (ts)

Issue:
FR: Support Obsidian-style comment for inline linter toggle
## Is Your Feature Request Related to a Problem? Please Describe.

Writing HTML comments (`<!-- -->`), aside from being tedious normally, is especially annoying when using software or plugins that attempt to auto-correct successive hyphens and other characters into distinct typographical marks. For example, [Smart Typography](https://github.com/mgmeyers/obsidian-smart-typography) ([view in Obsidian](https://r.0xyz.com/?to=obsidian%3A%2F%2Fshow-plugin%3Fid%3Dobsidian-smart-typography)) makes typing an HTML comment look something like this:
`<!----` *`<BS>`* `linter-disable----` *`<BS>`* `>` *`<BS>`*

Is that not a problem of this plugin? Absolutely. But only looking at HTML comments ignores Obsidian's commenting syntax which could be used as an alternative, for more reasons than just this one.

## Describe the Solution You'd Like

Allow for `%%linter-(en|dis)able%%` as an alternative.

## Describe Alternatives You've Considered

Use templates instead of writing out the comment.

## Additional Context

#1122

Pull request description:
Support Obsidian-style comment for inline linter toggle
Fixes #1121

Patch:
diff --git a/docs/docs/usage/disabling-rules.md b/docs/docs/usage/disabling-rules.md
index c3d6a44..a2b4bd4 100644
--- a/docs/docs/usage/disabling-rules.md
+++ b/docs/docs/usage/disabling-rules.md
@@ -49,7 +49,7 @@ disabled rules: [all]
 ### Range Ignore
 
 When there is a need to disable the Linter for part of a file, ranged ignores can be used. The syntax for a ranged ignore
-is `<!-- linter-disable -->` with an optional `<!-- linter-enable -->` where you want the Linter to start back up with its linting.
+is `<!-- linter-disable -->` or `%%linter-disable%%` with an optional `<!-- linter-enable -->` or `%%linter-disable%%` where you want the Linter to start back up with its linting.
 Leaving off the ending of a range ignore will assume you want to ignore the file contents from the start of the range ignore to the end of the file. So be careful when not ending a range ignore.
 
 !!! warning
@@ -62,6 +62,9 @@ Here is some text
                           This area will not be formatted
 <!-- linter-enable -->
 More content goes here...
+%%linter-disable %%
+                          This area will not be formatted
+%%linter-enable%%
 ```
 
 Here is another example that shows a ranged ignore without an ending indicator:
diff --git a/src/utils/regex.ts b/src/utils/regex.ts
index 7560a31..59f2df4 100644
--- a/src/utils/regex.ts
+++ b/src/utils/regex.ts
@@ -133,7 +133,7 @@ export function matchTagRegex(text: string): string[] {
 }
 
 export function generateHTMLLinterCommentWithSpecificTextAndWhitespaceRegexMatch(isStart: boolean): RegExp {
-  const regexTemplate = '<!-{2,} *linter-{ENDING_TEXT} *-{2,}>';
+  const regexTemplate = '(?:<!-{2,}|%%) *linter-{ENDING_TEXT} *(?:-{2,}>|%%)';
   let endingText = '';
 
   if (isStart) {


Test patch:
diff --git a/__tests__/get-all-custom-ignore-sections-in-text.test.ts b/__tests__/get-all-custom-ignore-sections-in-text.test.ts
index 0dbda3f..ce32014 100644
--- a/__tests__/get-all-custom-ignore-sections-in-text.test.ts
+++ b/__tests__/get-all-custom-ignore-sections-in-text.test.ts
@@ -28,6 +28,16 @@ const getCustomIgnoreSectionsInTextTestCases: customIgnoresInTextTestCase[] = [
     expectedCustomIgnoresInText: 0,
     expectedPositions: [],
   },
+  {
+    name: 'when no custom ignore start indicator is present, no positions are returned even if custom ignore end indicator is present when Obsidian comment format is used',
+    text: dedent`
+      Here is some text
+      %% linter-enable %%
+      Here is some more text
+    `,
+    expectedCustomIgnoresInText: 0,
+    expectedPositions: [],
+  },
   {
     name: 'a simple example of a start and end custom ignore indicator results in the proper start and end positions for the ignore section',
     text: dedent`
@@ -41,6 +51,19 @@ const getCustomIgnoreSectionsInTextTestCases: customIgnoresInTextTestCase[] = [
     expectedCustomIgnoresInText: 1,
     expectedPositions: [{startIndex: 18, endIndex: 135}],
   },
+  {
+    name: 'a simple example of a start and end custom ignore indicator results in the proper start and end positions for the ignore section when Obsidian comment format is used',
+    text: dedent`
+      Here is some text
+      %% linter-disable %%
+      This content will be ignored
+      So any format put here gets to stay as is
+      %% linter-enable %%
+      More text here...
+    `,
+    expectedCustomIgnoresInText: 1,
+    expectedPositions: [{startIndex: 18, endIndex: 129}],
+  },
   {
     name: 'when a custom ignore start indicator is not followed by a custom ignore end indicator in the text, the end is considered to be the end of the text',
     text: dedent`
@@ -53,6 +76,18 @@ const getCustomIgnoreSectionsInTextTestCases: customIgnoresInTextTestCase[] = [
     expectedCustomIgnoresInText: 1,
     expectedPositions: [{startIndex: 18, endIndex: 129}],
   },
+  {
+    name: 'when a custom ignore start indicator is not followed by a custom ignore end indicator in the text, the end is considered to be the end of the text when Obsidian comment format is used',
+    text: dedent`
+      Here is some text
+      %% linter-disable %%
+      This content will be ignored
+      So any format put here gets to stay as is
+      More text here...
+    `,
+    expectedCustomIgnoresInText: 1,
+    expectedPositions: [{startIndex: 18, endIndex: 126}],
+  },
   {
     name: 'when a custom ignore start indicator shows up midline, it ignores the part in question',
     text: dedent`
@@ -64,6 +99,17 @@ const getCustomIgnoreSectionsInTextTestCases: customIgnoresInTextTestCase[] = [
     expectedCustomIgnoresInText: 1,
     expectedPositions: [{startIndex: 17, endIndex: 87}],
   },
+  {
+    name: 'when a custom ignore start indicator shows up midline, it ignores the part in question when Obsidian comment format is used',
+    text: dedent`
+      Here is some text%% linter-disable %%here is some ignored text%% linter-enable %%
+      This content will be ignored
+      So any format put here gets to stay as is
+      More text here...
+    `,
+    expectedCustomIgnoresInText: 1,
+    expectedPositions: [{startIndex: 17, endIndex: 81}],
+  },
   {
     name: 'when a custom ignore start indicator does not follow the exact syntax, it is counted as existing when it is a single-line comment',
     text: dedent`
@@ -93,8 +139,26 @@ const getCustomIgnoreSectionsInTextTestCases: customIgnoresInTextTestCase[] = [
     expectedCustomIgnoresInText: 2,
     expectedPositions: [{startIndex: 178, endIndex: 316}, {startIndex: 17, endIndex: 87}],
   },
+  {
+    name: 'multiple matches can be returned when Obsidian comment format is used',
+    text: dedent`
+      Here is some text%% linter-disable %%here is some ignored text%% linter-enable %%
+      This content will be ignored
+      So any format put here gets to stay as is
+      More text here...
+      ${''}
+      %% linter-disable %%
+      We want to ignore the following as we want to preserve its format
+        -> level 1
+          -> level 1.3
+        -> level 2
+      Finish
+    `,
+    expectedCustomIgnoresInText: 2,
+    expectedPositions: [{startIndex: 172, endIndex: 307}, {startIndex: 17, endIndex: 81}],
+  },
   { // relates to https://github.com/platers/obsidian-linter/issues/733
-    name: 'multiple matches can be returned',
+    name: 'multiple matches can be returned with math blocks',
     text: dedent`
       content
       ${''}
@@ -123,6 +187,36 @@ const getCustomIgnoreSectionsInTextTestCases: customIgnoresInTextTestCase[] = [
     expectedCustomIgnoresInText: 2,
     expectedPositions: [{startIndex: 86, endIndex: 152}, {startIndex: 9, endIndex: 75}],
   },
+  {
+    name: 'multiple matches can be returned with math blocks when Obsidian comment format is used',
+    text: dedent`
+      content
+      ${''}
+      %% linter-disable %%
+      ${''}
+      $$
+      abc
+      $$
+      {#eq:a}
+      ${''}
+      %% linter-enable %%
+      ${''}
+      content
+      ${''}
+      %% linter-disable %%
+      ${''}
+      $$
+      abc
+      $$
+      {#eq:b}
+      ${''}
+      %% linter-enable %%
+      ${''}
+      content
+    `,
+    expectedCustomIgnoresInText: 2,
+    expectedPositions: [{startIndex: 80, endIndex: 140}, {startIndex: 9, endIndex: 69}],
+  },
 ];
 
 describe('Get All Custom Ignore Sections in Text', () => {
diff --git a/__tests__/ignore-list-of-types.test.ts b/__tests__/ignore-list-of-types.test.ts
index 5fcd7cf..b912afa 100644
--- a/__tests__/ignore-list-of-types.test.ts
+++ b/__tests__/ignore-list-of-types.test.ts
@@ -59,6 +59,44 @@ const ignoreListOfTypesTestCases: customIgnoresInTextTestCase[] = [
     `,
     ignoreTypes: [IgnoreTypes.customIgnore],
   },
+  {
+    name: 'when no custom ignore ranges are used and multiple times, the text is properly replaced and put back together when Obsidian comment format used',
+    text: dedent`
+      content
+      ${''}
+      %% linter-disable %%
+      ${''}
+      $$
+      abc
+      $$
+      ${''}
+      %% linter-enable %%
+      ${''}
+      content
+      ${''}
+      %% linter-disable %%
+      ${''}
+      $$
+      abc
+      $$
+      ${''}
+      %% linter-enable %%
+      ${''}
+      content
+    `,
+    expectedTextAfterIgnore: dedent`
+      content
+      ${''}
+      {CUSTOM_IGNORE_PLACEHOLDER}
+      ${''}
+      content
+      ${''}
+      {CUSTOM_IGNORE_PLACEHOLDER}
+      ${''}
+      content
+    `,
+    ignoreTypes: [IgnoreTypes.customIgnore],
+  },
 ];
 
 describe('Ignore List of Types', () => {
diff --git a/__tests__/rules-runner.test.ts b/__tests__/rules-runner.test.ts
index 25eee36..6dfaec0 100644
--- a/__tests__/rules-runner.test.ts
+++ b/__tests__/rules-runner.test.ts
@@ -224,6 +224,26 @@ const customReplaceTestCases: CustomReplaceTestCase[] = [
       Did it stay the same?
     `,
   },
+  { // relates for https://github.com/platers/obsidian-linter/issues/1121
+    testName: 'A custom replace should respect linter ignore ranges that use the Obsidian comment format',
+    listOfRegexReplacements: [
+      {
+        label: 'Replace Did at the start of a line or look? at the end of a line', find: '(^Did)|(look\\?$)', replace: 'swapped', flags: 'gm',
+      },
+    ],
+    before: dedent`
+      How does this look?
+      %% linter-disable %%
+      Did it stay the same?
+      %% linter-enable %%
+    `,
+    after: dedent`
+      How does this swapped
+      %% linter-disable %%
+      Did it stay the same?
+      %% linter-enable %%
+    `,
+  },
 ];
 
 describe('Rules Runner', () => {