Skip to content

Commit 62f9e9a

Browse files
Simplify document parsing
Cleanup
1 parent ecba5c2 commit 62f9e9a

File tree

3 files changed

+103
-28
lines changed

3 files changed

+103
-28
lines changed

src/ankiConnectUtil.ts

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ enum ConnAction {
1414
CREATE_DECK = "createDeck",
1515
ADD_NOTE = "addNote",
1616
DECK_NAMES = "deckNames",
17+
FIND_NOTE = "findNotes"
1718
}
1819

1920
interface ConnResult {
@@ -25,13 +26,13 @@ interface ConnRequest {
2526
version: number
2627
}
2728

28-
export interface ConnNote {
29+
export interface Note {
2930
deckName: string,
3031
modelName: string,
31-
fields: ConnNoteFields
32+
fields: NoteFields
3233
}
3334

34-
export interface ConnNoteFields {
35+
export interface NoteFields {
3536
Front: string,
3637
Back: string
3738
}
@@ -51,16 +52,25 @@ export interface CreateDeckResult extends ConnResult {
5152

5253
export interface AddNoteRequest extends ConnRequest {
5354
params: {
54-
note: ConnNote
55+
note: Note
5556
}
5657
}
5758
export interface AddNoteResult extends ConnResult {
5859
result: number;
5960
}
6061

62+
export interface FindNoteRequest extends ConnRequest {
63+
params: {
64+
query: string
65+
}
66+
}
67+
export interface FindNoteResult extends ConnResult {
68+
result: number[];
69+
}
70+
6171
const BASE_REQUEST = { url: ANKI_CONN_URL, method: ANKI_CONN_METHOD };
6272

63-
export function buildNote(Front: string, Back: string): ConnNote {
73+
export function buildNote(Front: string, Back: string): Note {
6474
return {
6575
deckName: TARGET_DECK,
6676
modelName: DEFAULT_DECK_TYPE,
@@ -77,7 +87,7 @@ export async function sendCreateDeckRequest(deck: string): Promise<CreateDeckRes
7787
return res.json as CreateDeckResult
7888
}
7989

80-
export async function sendAddNoteRequest(note: ConnNote): Promise<AddNoteResult> {
90+
export async function sendAddNoteRequest(note: Note): Promise<AddNoteResult> {
8191
const req: AddNoteRequest = {
8292
action: ConnAction.ADD_NOTE,
8393
version: ANKI_CONN_VERSION,
@@ -87,6 +97,18 @@ export async function sendAddNoteRequest(note: ConnNote): Promise<AddNoteResult>
8797
return res.json as CreateDeckResult
8898
}
8999

100+
export async function sendFindNoteRequest(query: string): Promise<FindNoteResult> {
101+
const req: FindNoteRequest = {
102+
action: ConnAction.FIND_NOTE,
103+
version: ANKI_CONN_VERSION,
104+
params: {
105+
query
106+
}
107+
}
108+
const res = await buildAndSend(req);
109+
return res.json as FindNoteResult;
110+
}
111+
90112
export async function sendDeckNamesRequest(): Promise<DeckNamesResult> {
91113
const res = await buildAndSend({
92114
action: ConnAction.DECK_NAMES,

src/main.ts

Lines changed: 73 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
import { Notice, Plugin } from "obsidian";
1+
import { Notice, Plugin, TFile, Vault } from "obsidian";
22
import {
33
DEFAULT_SETTINGS,
44
AnkiLinkSettings,
55
AnkiLinkSettingsTab,
66
} 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+
}
915

1016
export default class AnkiLink extends Plugin {
1117
settings!: AnkiLinkSettings;
@@ -65,34 +71,80 @@ export default class AnkiLink extends Plugin {
6571
const createDeckRes = await sendCreateDeckRequest(TARGET_DECK);
6672
if (createDeckRes.error) throw new Error(`AnkiConnect: ${createDeckRes.error}`)
6773
}
74+
let totalAdded = 0;
6875
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) {
72107
continue;
73108
}
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;
83115
}
84-
return 0;
116+
return output;
85117
}
86118

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]!}
91136
}
92137

93-
private async sendNote(note: ConnNote) {
138+
private async sendNote(note: Note) {
94139
const res = await sendAddNoteRequest(note);
95140
if (res.error) throw new Error(`AnkiConnect ${res.error}`);
96141
return res.result;
97142
}
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+
}
98150
}

src/regexUtil.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

2-
export const FLASHCARD_PATTERN: RegExp = /\[!flashcard\]\s*([^\n]*)\n((?:>(?:[^\n]*)\n?)*)/;
2+
export const FC_PREAMBLE_P = /> \[!flashcard\]\s*(?:%%(\d+)%%)?\s*(.*)/;
3+
export const FC_CALLOUT_LENGTH = 14;
34

45
export function splitCalloutBody(body: string) {
56
const lines = body.split(">");

0 commit comments

Comments
 (0)