Add support for custom inline separators #61

This commit is contained in:
alexcolucci@protonmail.com 2022-02-03 11:11:23 +01:00
parent efad21d7d7
commit 90a54a1aa9
6 changed files with 77 additions and 12 deletions

15
main.ts
View File

@ -3,8 +3,7 @@ import { ISettings } from 'src/settings';
import { SettingsTab } from 'src/gui/settings-tab';
import { CardsService } from 'src/services/cards';
import { Anki } from 'src/services/anki';
import { noticeTimeout } from 'src/constants'
import { flashcardsIcon } from 'src/constants'
import { noticeTimeout, flashcardsIcon } from 'src/constants';
export default class ObsidianFlashcard extends Plugin {
private settings: ISettings
@ -14,17 +13,17 @@ export default class ObsidianFlashcard extends Plugin {
addIcon("flashcards", flashcardsIcon)
// TODO test when file did not insert flashcards, but one of them is in Anki already
let anki = new Anki()
const anki = new Anki()
this.settings = await this.loadData() || this.getDefaultSettings()
this.cardsService = new CardsService(this.app, this.settings)
let statusBar = this.addStatusBarItem()
const statusBar = this.addStatusBarItem()
this.addCommand({
id: 'generate-flashcard-current-file',
name: 'Generate for the current file',
checkCallback: (checking: boolean) => {
let activeFile = this.app.workspace.getActiveFile()
const activeFile = this.app.workspace.getActiveFile()
if (activeFile) {
if (!checking) {
this.generateCards(activeFile)
@ -36,7 +35,7 @@ export default class ObsidianFlashcard extends Plugin {
});
this.addRibbonIcon('flashcards', 'Generate flashcards', () => {
let activeFile = this.app.workspace.getActiveFile()
const activeFile = this.app.workspace.getActiveFile()
if (activeFile) {
this.generateCards(activeFile)
} else {
@ -56,12 +55,12 @@ export default class ObsidianFlashcard extends Plugin {
}
private getDefaultSettings(): ISettings {
return { contextAwareMode: true, sourceSupport: false, codeHighlightSupport: false, inlineID: false, contextSeparator: " > ", deck: "Default", flashcardsTag: "card", defaultAnkiTag: "obsidian" }
return { contextAwareMode: true, sourceSupport: false, codeHighlightSupport: false, inlineID: false, contextSeparator: " > ", deck: "Default", flashcardsTag: "card", inlineSeparator: "::", inlineSeparatorReverse: ":::", defaultAnkiTag: "obsidian" }
}
private generateCards(activeFile: TFile) {
this.cardsService.execute(activeFile).then(res => {
for (let r of res) {
for (const r of res) {
new Notice(r, noticeTimeout)
}
console.log(res)

View File

@ -1,5 +1,6 @@
import { Notice, PluginSettingTab, Setting } from "obsidian";
import { Anki } from "src/services/anki";
import { escapeRegExp } from "src/utils";
export class SettingsTab extends PluginSettingTab {
display(): void {
@ -103,6 +104,60 @@ export class SettingsTab extends PluginSettingTab {
});
});
new Setting(containerEl)
.setName("Inline card separator")
.setDesc(
"The separator to identifty the inline cards in the notes."
)
.addText((text) => {
text
.setValue(plugin.settings.inlineSeparator)
.setPlaceholder("::")
.onChange((value) => {
// if the value is empty or is the same like the inlineseparatorreverse then set it to the default, otherwise save it
if (value.trim().length === 0 || value === plugin.settings.inlineSeparatorReverse) {
plugin.settings.inlineSeparator = "::";
if (value.trim().length === 0) {
new Notice("The separator must be at least 1 character long");
} else if (value === plugin.settings.inlineSeparatorReverse) {
new Notice("The separator must be different from the inline reverse separator");
}
} else {
plugin.settings.inlineSeparator = escapeRegExp(value.trim());
new Notice("The separator has been changed");
}
plugin.saveData(plugin.settings);
});
});
new Setting(containerEl)
.setName("Inline reverse card separator")
.setDesc(
"The separator to identifty the inline revese cards in the notes."
)
.addText((text) => {
text
.setValue(plugin.settings.inlineSeparatorReverse)
.setPlaceholder(":::")
.onChange((value) => {
// if the value is empty or is the same like the inlineseparatorreverse then set it to the default, otherwise save it
if (value.trim().length === 0 || value === plugin.settings.inlineSeparator) {
plugin.settings.inlineSeparatorReverse = ":::";
if (value.trim().length === 0) {
new Notice("The separator must be at least 1 character long");
} else if (value === plugin.settings.inlineSeparator) {
new Notice("The separator must be different from the inline separator");
}
} else {
plugin.settings.inlineSeparatorReverse = escapeRegExp(value.trim());
new Notice("The separator has been changed");
}
plugin.saveData(plugin.settings);
});
});
new Setting(containerEl)
.setName("Default Anki tag")
.setDesc("This tag will be added to each generated card on Anki")

View File

@ -50,13 +50,17 @@ export class Regex {
"(?:[/-]reverse)?)((?: *#[\\p{Letter}\\-\\/_]+)*) *?\\n+((?:[^\\n]\\n?)*?(?=\\^\\d{13}|$))(?:\\^(\\d{13}))?";
this.flashscardsWithTag = new RegExp(str, flags);
// https://regex101.com/r/Ixtzlv/1
// https://regex101.com/r/8wmOo8/1
const sepLongest = settings.inlineSeparator.length >= settings.inlineSeparatorReverse.length ? settings.inlineSeparator : settings.inlineSeparatorReverse;
const sepShortest = settings.inlineSeparator.length < settings.inlineSeparatorReverse.length ? settings.inlineSeparator : settings.inlineSeparatorReverse;
// sepLongest is the longest between the inlineSeparator and the inlineSeparatorReverse because if the order is ::|::: then always the first will be matched
// sepShortest is the shortest
if (settings.inlineID) {
str =
"( {0,3}[#]{0,6})?(?:(?:[\\t ]*)(?:\\d.|[-+*]|#{1,6}))?(.+?) ?(:{2,3}) ?(.+?)((?: *#[\\p{Letter}\\-\\/_]+)+)?(?:\\s+\\^(\\d{13})|$)";
"( {0,3}[#]{0,6})?(?:(?:[\\t ]*)(?:\\d.|[-+*]|#{1,6}))?(.+?) ?(" + sepLongest + "|" + sepShortest + ") ?(.+?)((?: *#[\\p{Letter}\\-\\/_]+)+)?(?:\\s+\\^(\\d{13})|$)";
} else {
str =
"( {0,3}[#]{0,6})?(?:(?:[\\t ]*)(?:\\d.|[-+*]|#{1,6}))?(.+?) ?(:{2,3}) ?(.+?)((?: *#[\\p{Letter}\\-\\/_]+)+|$)(?:\\n\\^(\\d{13}))?";
"( {0,3}[#]{0,6})?(?:(?:[\\t ]*)(?:\\d.|[-+*]|#{1,6}))?(.+?) ?(" + sepLongest + "|" + sepShortest + ") ?(.+?)((?: *#[\\p{Letter}\\-\\/_]+)+|$)(?:\\n\\^(\\d{13}))?";
}
this.cardsInlineStyle = new RegExp(str, flags);

View File

@ -191,7 +191,7 @@ export class Parser {
continue;
}
const reversed: boolean = match[3].trim().toLowerCase() === ":::";
const reversed: boolean = match[3] === this.settings.inlineSeparatorReverse;
let headingLevel = -1;
if (match[1]) {
headingLevel =

View File

@ -6,5 +6,7 @@ export interface ISettings {
contextSeparator: string;
deck: string;
flashcardsTag: string;
inlineSeparator: string;
inlineSeparatorReverse: string;
defaultAnkiTag: string;
}

View File

@ -44,3 +44,8 @@ export function escapeMarkdown(string: string, skips: string[] = []) {
: s.replace(replacement[0], replacement[1]);
}, string);
}
export function escapeRegExp(str: string) {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
}