Repository: st3v3nmw/obsidian-spaced-repetition (ts)

Issue:
[FEAT] Use bold text as cloze test along highlighted one
## Feature
* Use ** or Bold text for **cloze**  card
## Why
* In most of the websites, bold texts can be directly copied to the notes, and most of the time those bold texts are the important facts/figures to remember
## Solution
* add a toggle in the plugin setting, where bold and highlight as cloze text could be togggled
* add a toggle
  * use both ** bold** and ==Highlight==

Pull request:
Add bolded to cloze cards conversion
- Closes #238 

Patch:
diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts
index 2412dfe..9b0cae6 100644
--- a/src/lang/locale/en.ts
+++ b/src/lang/locale/en.ts
@@ -42,7 +42,7 @@ export default {
 
     // settings.ts
     SETTINGS_HEADER: "Spaced Repetition Plugin - Settings",
-    CHECK_WIKI: 'For more information, check the <a href="${wiki_url}">wiki</a>',
+    CHECK_WIKI: 'For more information, check the <a href="${wiki_url}">wiki</a>.',
     FOLDERS_TO_IGNORE: "Folders to ignore",
     FOLDERS_TO_IGNORE_DESC: "Enter folder paths separated by newlines i.e. Templates Meta/Scripts",
     FLASHCARDS: "Flashcards",
@@ -91,7 +91,7 @@ export default {
     VALID_NUMBER_WARNING: "Please provide a valid number.",
     ALGORITHM: "Algorithm",
     CHECK_ALGORITHM_WIKI:
-        'For more information, check the <a href="${algo_url}">algorithm implementation</a>',
+        'For more information, check the <a href="${algo_url}">algorithm implementation</a>.',
     BASE_EASE: "Base ease",
     BASE_EASE_DESC: "minimum = 130, preferrably approximately 250.",
     BASE_EASE_MIN_WARNING: "The base ease must be at least 130.",
diff --git a/src/main.ts b/src/main.ts
index 65691e6..3e394c2 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -598,24 +598,23 @@ export default class SRPlugin extends Plugin {
             deckAdded = false,
             totalNoteEase = 0,
             scheduledCount = 0;
+        const settings: SRSettings = this.data.settings;
 
         const now: number = Date.now();
         const parsedCards: [CardType, string, number][] = parse(
             fileText,
-            this.data.settings.singlelineCardSeparator,
-            this.data.settings.singlelineReversedCardSeparator,
-            this.data.settings.multilineCardSeparator,
-            this.data.settings.multilineReversedCardSeparator
+            settings.singlelineCardSeparator,
+            settings.singlelineReversedCardSeparator,
+            settings.multilineCardSeparator,
+            settings.multilineReversedCardSeparator,
+            settings.convertHighlightsToClozes,
+            settings.convertBoldTextToClozes
         );
         for (const parsedCard of parsedCards) {
             const cardType: CardType = parsedCard[0],
                 cardText: string = parsedCard[1],
                 lineNo: number = parsedCard[2];
 
-            if (cardType === CardType.Cloze && this.data.settings.disableClozeCards) {
-                continue;
-            }
-
             const cardTextHash: string = cyrb53(cardText);
 
             if (buryOnly) {
@@ -630,55 +629,68 @@ export default class SRPlugin extends Plugin {
 
             const siblingMatches: [string, string][] = [];
             if (cardType === CardType.Cloze) {
+                const siblings: RegExpMatchArray[] = [];
+                if (settings.convertHighlightsToClozes) {
+                    siblings.push(...cardText.matchAll(/==(.*?)==/gm));
+                }
+                if (settings.convertBoldTextToClozes) {
+                    siblings.push(...cardText.matchAll(/\*\*(.*?)\*\*/gm));
+                }
+                siblings.sort((a, b) => {
+                    if (a.index < b.index) {
+                        return -1;
+                    }
+                    if (a.index > b.index) {
+                        return 1;
+                    }
+                    return 0;
+                });
+
                 let front: string, back: string;
-                for (const m of cardText.matchAll(/==(.*?)==/gm)) {
+                for (const m of siblings) {
                     const deletionStart: number = m.index,
                         deletionEnd: number = deletionStart + m[0].length;
                     front =
                         cardText.substring(0, deletionStart) +
                         "<span style='color:#2196f3'>[...]</span>" +
                         cardText.substring(deletionEnd);
-                    front = front.replace(/==/gm, "");
+                    front = front.replace(/==/gm, "").replace(/\*\*/gm, "");
                     back =
                         cardText.substring(0, deletionStart) +
                         "<span style='color:#2196f3'>" +
                         cardText.substring(deletionStart, deletionEnd) +
                         "</span>" +
                         cardText.substring(deletionEnd);
-                    back = back.replace(/==/gm, "");
+                    back = back.replace(/==/gm, "").replace(/\*\*/gm, "");
                     siblingMatches.push([front, back]);
                 }
             } else {
                 let idx: number;
                 if (cardType === CardType.SingleLineBasic) {
-                    idx = cardText.indexOf(this.data.settings.singlelineCardSeparator);
+                    idx = cardText.indexOf(settings.singlelineCardSeparator);
                     siblingMatches.push([
                         cardText.substring(0, idx),
-                        cardText.substring(idx + this.data.settings.singlelineCardSeparator.length),
+                        cardText.substring(idx + settings.singlelineCardSeparator.length),
                     ]);
                 } else if (cardType === CardType.SingleLineReversed) {
-                    idx = cardText.indexOf(this.data.settings.singlelineReversedCardSeparator);
+                    idx = cardText.indexOf(settings.singlelineReversedCardSeparator);
                     const side1: string = cardText.substring(0, idx),
                         side2: string = cardText.substring(
-                            idx + this.data.settings.singlelineReversedCardSeparator.length
+                            idx + settings.singlelineReversedCardSeparator.length
                         );
                     siblingMatches.push([side1, side2]);
                     siblingMatches.push([side2, side1]);
                 } else if (cardType === CardType.MultiLineBasic) {
-                    idx = cardText.indexOf("\n" + this.data.settings.multilineCardSeparator + "\n");
+                    idx = cardText.indexOf("\n" + settings.multilineCardSeparator + "\n");
                     siblingMatches.push([
                         cardText.substring(0, idx),
-                        cardText.substring(
-                            idx + 2 + this.data.settings.multilineCardSeparator.length
-                        ),
+                        cardText.substring(idx + 2 + settings.multilineCardSeparator.length),
                     ]);
                 } else if (cardType === CardType.MultiLineReversed) {
-                    idx = cardText.indexOf(
-                        "\n" + this.data.settings.multilineReversedCardSeparator + "\n"
-                    );
+                    idx = cardText.indexOf("\n" + settings.multilineReversedCardSeparator + "\n");
                     const side1: string = cardText.substring(0, idx),
                         side2: string = cardText.substring(
-                            idx + 2 + this.data.settings.multilineReversedCardSeparator.length
+                            idx + 2 + settings.multilineReversedCardSeparator.length
                         );
                     siblingMatches.push([side1, side2]);
                     siblingMatches.push([side2, side1]);
@@ -702,7 +714,7 @@ export default class SRPlugin extends Plugin {
                 fileChanged = true;
             }
 
-            const context: string = this.data.settings.showContextInCards
+            const context: string = settings.showContextInCards
                 ? getCardContext(lineNo, headings)
                 : "";
             const siblings: Card[] = [];
@@ -792,7 +804,7 @@ export default class SRPlugin extends Plugin {
             );
             return (
                 flashcardsInNoteAvgEase * flashcardContribution +
-                this.data.settings.baseEase * (1.0 - flashcardContribution)
+                settings.baseEase * (1.0 - flashcardContribution)
             );
         }
 
diff --git a/src/parser.ts b/src/parser.ts
index 359cbd5..d5c0c22 100644
--- a/src/parser.ts
+++ b/src/parser.ts
@@ -15,7 +15,9 @@ export function parse(
     singlelineCardSeparator: string,
     singlelineReversedCardSeparator: string,
     multilineCardSeparator: string,
-    multilineReversedCardSeparator: string
+    multilineReversedCardSeparator: string,
+    convertHighlightsToClozes: boolean,
+    convertBoldTextToClozes: boolean
 ): [CardType, string, number][] {
     let cardText = "";
     const cards: [CardType, string, number][] = [];
@@ -59,7 +61,11 @@ export function parse(
             cards.push([cardType, cardText, lineNo]);
             cardType = null;
             cardText = "";
-        } else if (cardType === null && /==.*?==/gm.test(lines[i])) {
+        } else if (
+            cardType === null &&
+            ((convertHighlightsToClozes && /==.*?==/gm.test(lines[i])) ||
+                (convertBoldTextToClozes && /\*\*.*?\*\*/gm.test(lines[i])))
+        ) {
             cardType = CardType.Cloze;
             lineNo = i;
         } else if (lines[i] === multilineCardSeparator) {
diff --git a/src/settings.ts b/src/settings.ts
index af9d0cf..f4c127c 100644
--- a/src/settings.ts
+++ b/src/settings.ts
@@ -48,8 +48,8 @@ export const DEFAULT_SETTINGS: SRSettings = {
     flashcardWidthPercentage: Platform.isMobile ? 100 : 40,
     showFileNameInFileLink: false,
     randomizeCardOrder: true,
-    convertHighlightsToClozes: false,
-    convertBoldTextToClozes: true,
+    convertHighlightsToClozes: true,
+    convertBoldTextToClozes: false,
     singlelineCardSeparator: "::",
     singlelineReversedCardSeparator: ":::",
     multilineCardSeparator: "?",


Test patch:
diff --git a/tests/parser.test.ts b/tests/parser.test.ts
index f83f9b4..8409fe8 100644
--- a/tests/parser.test.ts
+++ b/tests/parser.test.ts
@@ -1,107 +1,150 @@
 import { parse } from "src/parser";
 import { CardType } from "src/types";
 
-const defaultSeparators: [string, string, string, string] = ["::", ":::", "?", "??"];
+const defaultArgs: [string, string, string, string, boolean, boolean] = [
+    "::",
+    ":::",
+    "?",
+    "??",
+    true,
+    true,
+];
 
 test("Test parsing of single line basic cards", () => {
-    expect(parse("Question::Answer", ...defaultSeparators)).toEqual([
+    expect(parse("Question::Answer", ...defaultArgs)).toEqual([
         [CardType.SingleLineBasic, "Question::Answer", 0],
     ]);
-    expect(parse("Question::Answer\n<!--SR:!2021-08-11,4,270-->", ...defaultSeparators)).toEqual([
+    expect(parse("Question::Answer\n<!--SR:!2021-08-11,4,270-->", ...defaultArgs)).toEqual([
         [CardType.SingleLineBasic, "Question::Answer\n<!--SR:!2021-08-11,4,270-->", 0],
     ]);
-    expect(parse("Question::Answer <!--SR:2021-08-11,4,270-->", ...defaultSeparators)).toEqual([
+    expect(parse("Question::Answer <!--SR:2021-08-11,4,270-->", ...defaultArgs)).toEqual([
         [CardType.SingleLineBasic, "Question::Answer <!--SR:2021-08-11,4,270-->", 0],
     ]);
-    expect(parse("Some text before\nQuestion ::Answer", ...defaultSeparators)).toEqual([
+    expect(parse("Some text before\nQuestion ::Answer", ...defaultArgs)).toEqual([
         [CardType.SingleLineBasic, "Question ::Answer", 1],
     ]);
-    expect(parse("#Title\n\nQ1::A1\nQ2:: A2", ...defaultSeparators)).toEqual([
+    expect(parse("#Title\n\nQ1::A1\nQ2:: A2", ...defaultArgs)).toEqual([
         [CardType.SingleLineBasic, "Q1::A1", 2],
         [CardType.SingleLineBasic, "Q2:: A2", 3],
     ]);
 });
 
 test("Test parsing of single line reversed cards", () => {
-    expect(parse("Question:::Answer", ...defaultSeparators)).toEqual([
+    expect(parse("Question:::Answer", ...defaultArgs)).toEqual([
         [CardType.SingleLineReversed, "Question:::Answer", 0],
     ]);
-    expect(parse("Some text before\nQuestion :::Answer", ...defaultSeparators)).toEqual([
+    expect(parse("Some text before\nQuestion :::Answer", ...defaultArgs)).toEqual([
         [CardType.SingleLineReversed, "Question :::Answer", 1],
     ]);
-    expect(parse("#Title\n\nQ1:::A1\nQ2::: A2", ...defaultSeparators)).toEqual([
+    expect(parse("#Title\n\nQ1:::A1\nQ2::: A2", ...defaultArgs)).toEqual([
         [CardType.SingleLineReversed, "Q1:::A1", 2],
         [CardType.SingleLineReversed, "Q2::: A2", 3],
     ]);
 });
 
 test("Test parsing of multi line basic cards", () => {
-    expect(parse("Question\n?\nAnswer", ...defaultSeparators)).toEqual([
+    expect(parse("Question\n?\nAnswer", ...defaultArgs)).toEqual([
         [CardType.MultiLineBasic, "Question\n?\nAnswer", 1],
     ]);
-    expect(parse("Question\n?\nAnswer <!--SR:!2021-08-11,4,270-->", ...defaultSeparators)).toEqual([
+    expect(parse("Question\n?\nAnswer <!--SR:!2021-08-11,4,270-->", ...defaultArgs)).toEqual([
         [CardType.MultiLineBasic, "Question\n?\nAnswer <!--SR:!2021-08-11,4,270-->", 1],
     ]);
-    expect(parse("Question\n?\nAnswer\n<!--SR:2021-08-11,4,270-->", ...defaultSeparators)).toEqual([
+    expect(parse("Question\n?\nAnswer\n<!--SR:2021-08-11,4,270-->", ...defaultArgs)).toEqual([
         [CardType.MultiLineBasic, "Question\n?\nAnswer\n<!--SR:2021-08-11,4,270-->", 1],
     ]);
-    expect(parse("Some text before\nQuestion\n?\nAnswer", ...defaultSeparators)).toEqual([
+    expect(parse("Some text before\nQuestion\n?\nAnswer", ...defaultArgs)).toEqual([
         [CardType.MultiLineBasic, "Some text before\nQuestion\n?\nAnswer", 2],
     ]);
-    expect(parse("Question\n?\nAnswer\nSome text after!", ...defaultSeparators)).toEqual([
+    expect(parse("Question\n?\nAnswer\nSome text after!", ...defaultArgs)).toEqual([
         [CardType.MultiLineBasic, "Question\n?\nAnswer\nSome text after!", 1],
     ]);
-    expect(
-        parse("#Title\n\nLine0\nQ1\n?\nA1\nAnswerExtra\n\nQ2\n?\nA2", ...defaultSeparators)
-    ).toEqual([
+    expect(parse("#Title\n\nLine0\nQ1\n?\nA1\nAnswerExtra\n\nQ2\n?\nA2", ...defaultArgs)).toEqual([
         [CardType.MultiLineBasic, "Line0\nQ1\n?\nA1\nAnswerExtra", 4],
         [CardType.MultiLineBasic, "Q2\n?\nA2", 9],
     ]);
 });
 
 test("Test parsing of multi line reversed cards", () => {
-    expect(parse("Question\n??\nAnswer", ...defaultSeparators)).toEqual([
+    expect(parse("Question\n??\nAnswer", ...defaultArgs)).toEqual([
         [CardType.MultiLineReversed, "Question\n??\nAnswer", 1],
     ]);
-    expect(parse("Some text before\nQuestion\n??\nAnswer", ...defaultSeparators)).toEqual([
+    expect(parse("Some text before\nQuestion\n??\nAnswer", ...defaultArgs)).toEqual([
         [CardType.MultiLineReversed, "Some text before\nQuestion\n??\nAnswer", 2],
     ]);
-    expect(parse("Question\n??\nAnswer\nSome text after!", ...defaultSeparators)).toEqual([
+    expect(parse("Question\n??\nAnswer\nSome text after!", ...defaultArgs)).toEqual([
         [CardType.MultiLineReversed, "Question\n??\nAnswer\nSome text after!", 1],
     ]);
-    expect(
-        parse("#Title\n\nLine0\nQ1\n??\nA1\nAnswerExtra\n\nQ2\n??\nA2", ...defaultSeparators)
-    ).toEqual([
-        [CardType.MultiLineReversed, "Line0\nQ1\n??\nA1\nAnswerExtra", 4],
-        [CardType.MultiLineReversed, "Q2\n??\nA2", 9],
-    ]);
+    expect(parse("#Title\n\nLine0\nQ1\n??\nA1\nAnswerExtra\n\nQ2\n??\nA2", ...defaultArgs)).toEqual(
+        [
+            [CardType.MultiLineReversed, "Line0\nQ1\n??\nA1\nAnswerExtra", 4],
+            [CardType.MultiLineReversed, "Q2\n??\nA2", 9],
+        ]
+    );
 });
 
 test("Test parsing of cloze cards", () => {
-    expect(parse("cloze ==deletion== test", ...defaultSeparators)).toEqual([
+    // ==highlights==
+    expect(parse("cloze ==deletion== test", ...defaultArgs)).toEqual([
         [CardType.Cloze, "cloze ==deletion== test", 0],
     ]);
-    expect(
-        parse("cloze ==deletion== test\n<!--SR:2021-08-11,4,270-->", ...defaultSeparators)
-    ).toEqual([[CardType.Cloze, "cloze ==deletion== test\n<!--SR:2021-08-11,4,270-->", 0]]);
-    expect(
-        parse("cloze ==deletion== test <!--SR:2021-08-11,4,270-->", ...defaultSeparators)
-    ).toEqual([[CardType.Cloze, "cloze ==deletion== test <!--SR:2021-08-11,4,270-->", 0]]);
-    expect(parse("==this== is a ==deletion==\n", ...defaultSeparators)).toEqual([
+    expect(parse("cloze ==deletion== test\n<!--SR:2021-08-11,4,270-->", ...defaultArgs)).toEqual([
+        [CardType.Cloze, "cloze ==deletion== test\n<!--SR:2021-08-11,4,270-->", 0],
+    ]);
+    expect(parse("cloze ==deletion== test <!--SR:2021-08-11,4,270-->", ...defaultArgs)).toEqual([
+        [CardType.Cloze, "cloze ==deletion== test <!--SR:2021-08-11,4,270-->", 0],
+    ]);
+    expect(parse("==this== is a ==deletion==\n", ...defaultArgs)).toEqual([
         [CardType.Cloze, "==this== is a ==deletion==", 0],
     ]);
     expect(
         parse(
             "some text before\n\na deletion on\nsuch ==wow==\n\n" +
                 "many text\nsuch surprise ==wow== more ==text==\nsome text after\n\nHmm",
-            ...defaultSeparators
+            ...defaultArgs
         )
     ).toEqual([
         [CardType.Cloze, "a deletion on\nsuch ==wow==", 3],
         [CardType.Cloze, "many text\nsuch surprise ==wow== more ==text==\nsome text after", 6],
     ]);
-    expect(parse("srdf ==", ...defaultSeparators)).toEqual([]);
-    expect(parse("lorem ipsum ==p\ndolor won==", ...defaultSeparators)).toEqual([]);
+    expect(parse("srdf ==", ...defaultArgs)).toEqual([]);
+    expect(parse("lorem ipsum ==p\ndolor won==", ...defaultArgs)).toEqual([]);
+    expect(parse("lorem ipsum ==dolor won=", ...defaultArgs)).toEqual([]);
+    // ==highlights== turned off
+    expect(parse("cloze ==deletion== test", "::", ":::", "?", "??", false, true)).toEqual([]);
+
+    // **bolded**
+    expect(parse("cloze **deletion** test", ...defaultArgs)).toEqual([
+        [CardType.Cloze, "cloze **deletion** test", 0],
+    ]);
+    expect(parse("cloze **deletion** test\n<!--SR:2021-08-11,4,270-->", ...defaultArgs)).toEqual([
+        [CardType.Cloze, "cloze **deletion** test\n<!--SR:2021-08-11,4,270-->", 0],
+    ]);
+    expect(parse("cloze **deletion** test <!--SR:2021-08-11,4,270-->", ...defaultArgs)).toEqual([
+        [CardType.Cloze, "cloze **deletion** test <!--SR:2021-08-11,4,270-->", 0],
+    ]);
+    expect(parse("**this** is a **deletion**\n", ...defaultArgs)).toEqual([
+        [CardType.Cloze, "**this** is a **deletion**", 0],
+    ]);
+    expect(
+        parse(
+            "some text before\n\na deletion on\nsuch **wow**\n\n" +
+                "many text\nsuch surprise **wow** more **text**\nsome text after\n\nHmm",
+            ...defaultArgs
+        )
+    ).toEqual([
+        [CardType.Cloze, "a deletion on\nsuch **wow**", 3],
+        [CardType.Cloze, "many text\nsuch surprise **wow** more **text**\nsome text after", 6],
+    ]);
+    expect(parse("srdf **", ...defaultArgs)).toEqual([]);
+    expect(parse("lorem ipsum **p\ndolor won**", ...defaultArgs)).toEqual([]);
+    expect(parse("lorem ipsum **dolor won*", ...defaultArgs)).toEqual([]);
+    // **bolded** turned off
+    expect(parse("cloze **deletion** test", "::", ":::", "?", "??", true, false)).toEqual([]);
+
+    // both
+    expect(parse("cloze **deletion** test ==another deletion==!", ...defaultArgs)).toEqual([
+        [CardType.Cloze, "cloze **deletion** test ==another deletion==!", 0],
+    ]);
 });
 
 test("Test parsing of a mix of card types", () => {
@@ -111,7 +154,7 @@ test("Test parsing of a mix of card types", () => {
                 "Duis magna arcu, eleifend rhoncus ==euismod non,==\nlaoreet vitae enim.\n\n" +
                 "Fusce placerat::velit in pharetra gravida\n\n" +
                 "Donec dapibus ullamcorper aliquam.\n??\nDonec dapibus ullamcorper aliquam.\n<!--SR:2021-08-11,4,270-->",
-            ...defaultSeparators
+            ...defaultArgs
         )
     ).toEqual([
         [
@@ -136,7 +179,7 @@ test("Test codeblocks", () => {
         parse(
             "How do you ... Python?\n?\n" +
                 "```\nprint('Hello World!')\nprint('Howdy?')\nlambda x: x[0]\n```",
-            ...defaultSeparators
+            ...defaultArgs
         )
     ).toEqual([
         [
@@ -152,7 +195,7 @@ test("Test codeblocks", () => {
         parse(
             "How do you ... Python?\n?\n" +
                 "```\nprint('Hello World!')\n\n\nprint('Howdy?')\n\nlambda x: x[0]\n```",
-            ...defaultSeparators
+            ...defaultArgs
         )
     ).toEqual([
         [
@@ -165,14 +208,15 @@ test("Test codeblocks", () => {
 });
 
 test("Test not parsing cards in HTML comments", () => {
-    expect(parse("<!--cloze ==deletion== test-->", ...defaultSeparators)).toEqual([]);
     expect(
-        parse("<!--\nQuestion\n?\nAnswer <!--SR:!2021-08-11,4,270-->\n-->", ...defaultSeparators)
+        parse("<!--\nQuestion\n?\nAnswer <!--SR:!2021-08-11,4,270-->\n-->", ...defaultArgs)
     ).toEqual([]);
     expect(
         parse(
             "<!--\nQuestion\n?\nAnswer <!--SR:!2021-08-11,4,270-->\n\n<!--cloze ==deletion== test-->-->",
-            ...defaultSeparators
+            ...defaultArgs
         )
     ).toEqual([]);
+    expect(parse("<!--cloze ==deletion== test-->", ...defaultArgs)).toEqual([]);
+    expect(parse("<!--cloze **deletion** test-->", ...defaultArgs)).toEqual([]);
 });