|
1 | | -import { Notice, Plugin } from "obsidian"; |
| 1 | +import { Notice, Plugin, TFile, Vault } from "obsidian"; |
2 | 2 | import { |
3 | 3 | DEFAULT_SETTINGS, |
4 | 4 | AnkiLinkSettings, |
5 | 5 | AnkiLinkSettingsTab, |
6 | 6 | } from "./settings"; |
7 | | -import { TARGET_DECK, sendAddNoteRequest, buildNote, sendCreateDeckRequest, sendDeckNamesRequest, ConnNoteFields, ConnNote } from "./ankiConnectUtil"; |
8 | | -import { FLASHCARD_PATTERN, splitCalloutBody } from "./regexUtil"; |
| 7 | +import { TARGET_DECK, sendAddNoteRequest, buildNote, sendCreateDeckRequest, sendDeckNamesRequest, Note, sendFindNoteRequest } from "./ankiConnectUtil"; |
| 8 | +import { FC_PREAMBLE_P } from "./regexUtil"; |
| 9 | + |
| 10 | +interface ParsedNoteData { |
| 11 | + id: number | undefined, |
| 12 | + index: number, |
| 13 | + note: Note |
| 14 | +} |
9 | 15 |
|
10 | 16 | export default class AnkiLink extends Plugin { |
11 | 17 | settings!: AnkiLinkSettings; |
@@ -65,34 +71,80 @@ export default class AnkiLink extends Plugin { |
65 | 71 | const createDeckRes = await sendCreateDeckRequest(TARGET_DECK); |
66 | 72 | if (createDeckRes.error) throw new Error(`AnkiConnect: ${createDeckRes.error}`) |
67 | 73 | } |
| 74 | + let totalAdded = 0; |
68 | 75 | for (const file of markdownFiles) { |
69 | | - const s = await vault.read(file); |
70 | | - const match = FLASHCARD_PATTERN.exec(s); |
71 | | - if (!match || match.length < 3) { |
| 76 | + const lines = await this.readFile(vault, file); |
| 77 | + let linesModified = false; |
| 78 | + const noteDataList = this.parseDocument(lines); |
| 79 | + for (const noteData of noteDataList) { |
| 80 | + if (noteData.id == undefined) { |
| 81 | + noteData.id = await this.sendNote(noteData.note); |
| 82 | + lines[noteData.index] = `> [!flashcard] %%{noteId}%% ${noteData.note.fields.Front}`; |
| 83 | + linesModified = true; |
| 84 | + totalAdded += 1; |
| 85 | + } else { |
| 86 | + //TODO: Check if the note has changed and update it. |
| 87 | + } |
| 88 | + } |
| 89 | + if (linesModified) { |
| 90 | + await vault.modify(file, lines.join("\n")); |
| 91 | + } |
| 92 | + } |
| 93 | + return totalAdded; |
| 94 | + } |
| 95 | + |
| 96 | + private async readFile(vault: Vault, file: TFile): Promise<string[]> { |
| 97 | + return (await vault.read(file)).split("\n"); |
| 98 | + } |
| 99 | + |
| 100 | + private parseDocument(lines: string[]): ParsedNoteData[] { |
| 101 | + const output = new Array<ParsedNoteData>(); |
| 102 | + let i = 0; |
| 103 | + while (i < lines.length) { |
| 104 | + i++; |
| 105 | + const { id, title } = this.parsePreamble(lines[i]!) || {}; |
| 106 | + if (!title) { |
72 | 107 | continue; |
73 | 108 | } |
74 | | - const title = match[1]!; |
75 | | - const rawBody = match[2]!; |
76 | | - const splitBody = splitCalloutBody(rawBody); |
77 | | - const card = buildNote(title, splitBody) |
78 | | - const index = await this.sendNote(card); |
79 | | - console.log(title.length); |
80 | | - const endIndex = match.index + title.length + 13; |
81 | | - const indexedFileContent = this.spliceString(s, endIndex, index.toString()); |
82 | | - await vault.modify(file, indexedFileContent); |
| 109 | + |
| 110 | + const bodyLines = this.parseBody(lines.slice(i, -1)); |
| 111 | + const body = bodyLines.join("<br>"); |
| 112 | + const note = buildNote(title, body); |
| 113 | + output.push({ id: id ? Number(id) : undefined, index: i, note }) |
| 114 | + i += bodyLines.length; |
83 | 115 | } |
84 | | - return 0; |
| 116 | + return output; |
85 | 117 | } |
86 | 118 |
|
87 | | - private spliceString(base: string, index: number, item: string): string { |
88 | | - const s1 = base.slice(0, index); |
89 | | - const s2 = base.slice(index, - 1) |
90 | | - return s1 + item + s2; |
| 119 | + private parseBody(lines: string[]) { |
| 120 | + const bodyLines: string[] = []; |
| 121 | + for (const line of lines) { |
| 122 | + if (!line.startsWith(">")) { |
| 123 | + return bodyLines; |
| 124 | + } |
| 125 | + bodyLines.push(line.replace(/^>\s?/, "")); |
| 126 | + } |
| 127 | + return bodyLines; |
| 128 | + } |
| 129 | + |
| 130 | + private parsePreamble(str: string) { |
| 131 | + const match = FC_PREAMBLE_P.exec(str); |
| 132 | + if (!match) { |
| 133 | + return undefined |
| 134 | + } |
| 135 | + return { id: match[1], title: match[2]!} |
91 | 136 | } |
92 | 137 |
|
93 | | - private async sendNote(note: ConnNote) { |
| 138 | + private async sendNote(note: Note) { |
94 | 139 | const res = await sendAddNoteRequest(note); |
95 | 140 | if (res.error) throw new Error(`AnkiConnect ${res.error}`); |
96 | 141 | return res.result; |
97 | 142 | } |
| 143 | + |
| 144 | + private async findNoteById(id: number) { |
| 145 | + const query =`nid:${id}`; |
| 146 | + const res = await sendFindNoteRequest(query); |
| 147 | + if (res.error) throw new Error(`AnkiConnect ${res.error}`); |
| 148 | + return res.result; |
| 149 | + } |
98 | 150 | } |
0 commit comments