Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tag integration and Folder settings #158

Merged
merged 14 commits into from
Dec 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export default class MyPlugin extends Plugin {
CUSTOM_REGEXPS: {},
FILE_LINK_FIELDS: {},
CONTEXT_FIELDS: {},
FOLDER_DECKS: {},
FOLDER_TAGS: {},
Syntax: {
"Begin Note": "START",
"End Note": "END",
Expand All @@ -38,6 +40,7 @@ export default class MyPlugin extends Plugin {
"CurlyCloze": false,
"CurlyCloze - Highlights to Clozes": false,
"ID Comments": true,
"Add Obsidian Tags": false,
}
}
/*Making settings from scratch, so need note types*/
Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "obsidian-to-anki-plugin",
"name": "Obsidian_to_Anki",
"version": "3.3.5",
"version": "3.4.0",
"minAppVersion": "0.9.20",
"description": "This is an Anki integration plugin! Designed for efficient bulk exporting.",
"author": "Pseudonium",
Expand Down
9 changes: 9 additions & 0 deletions src/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,15 @@ export class AllFile extends AbstractFile {

add_spans_to_ignore() {
this.ignore_spans = []
this.ignore_spans.push(...spans(this.data.FROZEN_REGEXP, this.file))
const deck_result = this.file.match(this.data.DECK_REGEXP)
if (deck_result) {
this.ignore_spans.push([deck_result.index, deck_result.index + deck_result[0].length])
}
const tag_result = this.file.match(this.data.TAG_REGEXP)
if (tag_result) {
this.ignore_spans.push([tag_result.index, tag_result.index + tag_result[0].length])
}
this.ignore_spans.push(...spans(this.data.NOTE_REGEXP, this.file))
this.ignore_spans.push(...spans(this.data.INLINE_REGEXP, this.file))
this.ignore_spans.push(...spans(c.OBS_INLINE_MATH_REGEXP, this.file))
Expand Down
88 changes: 70 additions & 18 deletions src/files-manager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*Class for managing a list of files, and their Anki requests.*/
import { ParsedSettings } from './interfaces/settings-interface'
import { App, TFile, CachedMetadata, FileSystemAdapter, Notice } from 'obsidian'
import { ParsedSettings, FileData } from './interfaces/settings-interface'
import { App, TFile, TFolder, TAbstractFile, CachedMetadata, FileSystemAdapter, Notice } from 'obsidian'
import { AllFile } from './file'
import * as AnkiConnect from './anki'
import { basename } from 'path'
Expand Down Expand Up @@ -73,41 +73,93 @@ export class FileManager {
return "obsidian://open?vault=" + encodeURIComponent(this.data.vault_name) + String.raw`&file=` + encodeURIComponent(file.path)
}

async initialiseFiles() {
await this.genAllFiles()
let files_changed: Array<AllFile> = []
let obfiles_changed: TFile[] = []
for (let index in this.ownFiles) {
const i = parseInt(index)
let file = this.ownFiles[i]
if (!(this.file_hashes.hasOwnProperty(file.path) && file.getHash() === this.file_hashes[file.path])) {
//Indicates it's changed or new
console.info("Scanning ", file.path, "as it's changed or new.")
file.scanFile()
files_changed.push(file)
obfiles_changed.push(this.files[i])
getFolderPathList(file: TFile): TFolder[] {
let result: TFolder[] = []
let abstractFile: TAbstractFile = file
while (abstractFile.hasOwnProperty('parent')) {
result.push(abstractFile.parent)
abstractFile = abstractFile.parent
}
result.pop() // Removes top-level vault
return result
}

getDefaultDeck(file: TFile, folder_path_list: TFolder[]): string {
let folder_decks = this.data.folder_decks
for (let folder of folder_path_list) {
// Loops over them from innermost folder
if (folder_decks[folder.path] !== "") {
return folder_decks[folder.path]
}
}
this.ownFiles = files_changed
this.files = obfiles_changed
// If no decks specified
return this.data.template.deckName
}

getDefaultTags(file: TFile, folder_path_list: TFolder[]): string[] {
let folder_tags = this.data.folder_tags
let tags_list: string[] = []
for (let folder of folder_path_list) {
// Loops over them from innermost folder
if (folder_tags[folder.path] !== "") {
tags_list.push(...folder_tags[folder.path].split(" "))
}
}
tags_list.push(...this.data.template.tags)
return tags_list
}

dataToFileData(file: TFile): FileData {
const folder_path_list: TFolder[] = this.getFolderPathList(file)
let result: FileData = JSON.parse(JSON.stringify(this.data))
//Lost regexp, so have to get them back
result.FROZEN_REGEXP = this.data.FROZEN_REGEXP
result.DECK_REGEXP = this.data.DECK_REGEXP
result.TAG_REGEXP = this.data.TAG_REGEXP
result.NOTE_REGEXP = this.data.NOTE_REGEXP
result.INLINE_REGEXP = this.data.INLINE_REGEXP
result.EMPTY_REGEXP = this.data.EMPTY_REGEXP
result.template.deckName = this.getDefaultDeck(file, folder_path_list)
result.template.tags = this.getDefaultTags(file, folder_path_list)
return result
}

async genAllFiles() {
for (let file of this.files) {
const content: string = await this.app.vault.read(file)
const cache: CachedMetadata = this.app.metadataCache.getCache(file.path)
const file_data = this.dataToFileData(file)
this.ownFiles.push(
new AllFile(
content,
file.path,
this.data.add_file_link ? this.getUrl(file) : "",
this.data,
file_data,
cache
)
)
}
}

async initialiseFiles() {
await this.genAllFiles()
let files_changed: Array<AllFile> = []
let obfiles_changed: TFile[] = []
for (let index in this.ownFiles) {
const i = parseInt(index)
let file = this.ownFiles[i]
if (!(this.file_hashes.hasOwnProperty(file.path) && file.getHash() === this.file_hashes[file.path])) {
//Indicates it's changed or new
console.info("Scanning ", file.path, "as it's changed or new.")
file.scanFile()
files_changed.push(file)
obfiles_changed.push(this.files[i])
}
}
this.ownFiles = files_changed
this.files = obfiles_changed
}

async requests_1() {
let requests: AnkiConnect.AnkiConnectRequest[] = []
let temp: AnkiConnect.AnkiConnectRequest[] = []
Expand Down
8 changes: 7 additions & 1 deletion src/interfaces/settings-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { AnkiConnectNote } from './note-interface'
export interface PluginSettings {
CUSTOM_REGEXPS: Record<string, string>,
FILE_LINK_FIELDS: Record<string, string>,
CONTEXT_FIELDS: Record<string, string>
CONTEXT_FIELDS: Record<string, string>,
FOLDER_DECKS: Record<string, string>,
FOLDER_TAGS: Record<string, string>,
Syntax: {
"Begin Note": string,
"End Note": string,
Expand All @@ -24,6 +26,7 @@ export interface PluginSettings {
"CurlyCloze": boolean,
"CurlyCloze - Highlights to Clozes": boolean,
"ID Comments": boolean,
"Add Obsidian Tags": boolean
}
}

Expand All @@ -48,8 +51,11 @@ export interface FileData {
highlights_to_cloze: boolean
comment: boolean
add_context: boolean
add_obs_tags: boolean
}

export interface ParsedSettings extends FileData {
add_file_link: boolean
folder_decks: Record<string, string>
folder_tags: Record<string, string>
}
9 changes: 9 additions & 0 deletions src/note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const TAG_PREFIX:string = "Tags: "
export const TAG_SEP:string = " "
export const ID_REGEXP_STR: string = String.raw`\n?(?:<!--)?(?:ID: (\d+).*)`
export const TAG_REGEXP_STR: string = String.raw`(Tags: .*)`
const OBS_TAG_REGEXP: RegExp = /#(\w+)/g

const ANKI_CLOZE_REGEXP: RegExp = /{{c\d+::[\s\S]+?}}/
export const CLOZE_ERROR: number = 42
Expand Down Expand Up @@ -86,6 +87,14 @@ abstract class AbstractNote {
const context_field = data.context_fields[this.note_type]
template["fields"][context_field] += context
}
if (data.add_obs_tags) {
for (let key in template["fields"]) {
for (let match of template["fields"][key].matchAll(OBS_TAG_REGEXP)) {
this.tags.push(match[1])
}
template["fields"][key] = template["fields"][key].replace(OBS_TAG_REGEXP, "")
}
}
template["tags"].push(...this.tags)
template["deckName"] = deck
return {note: template, identifier: this.identifier}
Expand Down
3 changes: 3 additions & 0 deletions src/setting-to-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export async function settingToData(app: App, settings: PluginSettings, fields_d
result.custom_regexps = settings.CUSTOM_REGEXPS
result.file_link_fields = settings.FILE_LINK_FIELDS
result.context_fields = settings.CONTEXT_FIELDS
result.folder_decks = settings.FOLDER_DECKS
result.folder_tags = settings.FOLDER_TAGS
result.template = {
deckName: settings.Defaults.Deck,
modelName: "",
Expand All @@ -39,6 +41,7 @@ export async function settingToData(app: App, settings: PluginSettings, fields_d
result.add_file_link = settings.Defaults["Add File Link"]
result.comment = settings.Defaults["ID Comments"]
result.add_context = settings.Defaults["Add Context"]
result.add_obs_tags = settings.Defaults["Add Obsidian Tags"]

return result
}
112 changes: 104 additions & 8 deletions src/settings.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PluginSettingTab, Setting, Notice } from 'obsidian'
import { PluginSettingTab, Setting, Notice, TFolder } from 'obsidian'
import * as AnkiConnect from './anki'

const defaultDescs = {
Expand All @@ -9,7 +9,8 @@ const defaultDescs = {
"Add Context": "Append 'context' for the card, in the form of path > heading > heading etc, to the field specified in the table.",
"CurlyCloze": "Convert {cloze deletions} -> {{c1::cloze deletions}} on note types that have a 'Cloze' in their name.",
"CurlyCloze - Highlights to Clozes": "Convert ==highlights== -> {highlights} to be processed by CurlyCloze.",
"ID Comments": "Wrap note IDs in a HTML comment."
"ID Comments": "Wrap note IDs in a HTML comment.",
"Add Obsidian Tags": "Interpret #tags in the fields of a note as Anki tags, removing them from the note text in Anki."
}

export class SettingsTab extends PluginSettingTab {
Expand Down Expand Up @@ -94,13 +95,11 @@ export class SettingsTab extends PluginSettingTab {
context_field.controlEl.className += " anki-center"
}

setup_table() {
create_collapsible(name: string) {
let {containerEl} = this;
const plugin = (this as any).plugin
containerEl.createEl('h3', {text: 'Note type settings'})
let div = containerEl.createEl('div', {cls: "collapsible-item"})
div.innerHTML = `
<div class="collapsible-item-self"><div class="collapsible-item-collapse collapse-icon anki-rotated"><svg viewBox="0 0 100 100" width="8" height="8" class="right-triangle"><path fill="currentColor" stroke="currentColor" d="M94.9,20.8c-1.4-2.5-4.1-4.1-7.1-4.1H12.2c-3,0-5.7,1.6-7.1,4.1c-1.3,2.4-1.2,5.2,0.2,7.6L43.1,88c1.5,2.3,4,3.7,6.9,3.7 s5.4-1.4,6.9-3.7l37.8-59.6C96.1,26,96.2,23.2,94.9,20.8L94.9,20.8z"></path></svg></div><div class="collapsible-item-inner"></div><header >Note Type Table</header></div>
<div class="collapsible-item-self"><div class="collapsible-item-collapse collapse-icon anki-rotated"><svg viewBox="0 0 100 100" width="8" height="8" class="right-triangle"><path fill="currentColor" stroke="currentColor" d="M94.9,20.8c-1.4-2.5-4.1-4.1-7.1-4.1H12.2c-3,0-5.7,1.6-7.1,4.1c-1.3,2.4-1.2,5.2,0.2,7.6L43.1,88c1.5,2.3,4,3.7,6.9,3.7 s5.4-1.4,6.9-3.7l37.8-59.6C96.1,26,96.2,23.2,94.9,20.8L94.9,20.8z"></path></svg></div><div class="collapsible-item-inner"></div><header>${name}</header></div>
`
div.addEventListener('click', function () {
this.classList.toggle("active")
Expand All @@ -113,6 +112,13 @@ export class SettingsTab extends PluginSettingTab {
content.style.display = "block"
}
})
}

setup_note_table() {
let {containerEl} = this;
const plugin = (this as any).plugin
containerEl.createEl('h3', {text: 'Note type settings'})
this.create_collapsible("Note Type Table")
let note_type_table = containerEl.createEl('table', {cls: "anki-settings-table"})
let head = note_type_table.createTHead()
let header_row = head.insertRow()
Expand Down Expand Up @@ -176,6 +182,10 @@ export class SettingsTab extends PluginSettingTab {
if (!(plugin.settings["Defaults"].hasOwnProperty("CurlyCloze - Highlights to Clozes"))) {
plugin.settings["Defaults"]["CurlyCloze - Highlights to Clozes"] = false
}
// To account for new add obsidian tags
if (!(plugin.settings["Defaults"].hasOwnProperty("Add Obsidian Tags"))) {
plugin.settings["Defaults"]["Add Obsidian Tags"] = false
}
for (let key of Object.keys(plugin.settings["Defaults"])) {
// To account for removal of regex setting
if (key === "Regex") {
Expand Down Expand Up @@ -230,12 +240,97 @@ export class SettingsTab extends PluginSettingTab {
}
}

get_folders(): TFolder[] {
const app = (this as any).plugin.app
let folder_list: TFolder[] = [app.vault.getRoot()]
for (let folder of folder_list) {
let filtered_list: TFolder[] = folder.children.filter((element) => element.hasOwnProperty("children")) as TFolder[]
folder_list.push(...filtered_list)
}
return folder_list.slice(1) //Removes initial vault folder
}

setup_folder_deck(folder: TFolder, row_cells: HTMLCollection) {
const plugin = (this as any).plugin
let folder_decks = plugin.settings.FOLDER_DECKS
if (!(folder_decks.hasOwnProperty(folder.path))) {
folder_decks[folder.path] = ""
}
let folder_deck = new Setting(row_cells[1] as HTMLElement)
.addText(
text => text.setValue(folder_decks[folder.path])
.onChange((value) => {
plugin.settings.FOLDER_DECKS[folder.path] = value
plugin.saveAllData()
})
)
folder_deck.settingEl = row_cells[1] as HTMLElement
folder_deck.infoEl.remove()
folder_deck.controlEl.className += " anki-center"
}

setup_folder_tag(folder: TFolder, row_cells: HTMLCollection) {
const plugin = (this as any).plugin
let folder_tags = plugin.settings.FOLDER_TAGS
if (!(folder_tags.hasOwnProperty(folder.path))) {
folder_tags[folder.path] = ""
}
let folder_tag = new Setting(row_cells[2] as HTMLElement)
.addText(
text => text.setValue(folder_tags[folder.path])
.onChange((value) => {
plugin.settings.FOLDER_TAGS[folder.path] = value
plugin.saveAllData()
})
)
folder_tag.settingEl = row_cells[2] as HTMLElement
folder_tag.infoEl.remove()
folder_tag.controlEl.className += " anki-center"
}

setup_folder_table() {
let {containerEl} = this;
const plugin = (this as any).plugin
const folder_list = this.get_folders()
containerEl.createEl('h3', {text: 'Folder settings'})
this.create_collapsible("Folder Table")
let folder_table = containerEl.createEl('table', {cls: "anki-settings-table"})
let head = folder_table.createTHead()
let header_row = head.insertRow()
for (let header of ["Folder", "Folder Deck", "Folder Tags"]) {
let th = document.createElement("th")
th.appendChild(document.createTextNode(header))
header_row.appendChild(th)
}
let main_body = folder_table.createTBody()
if (!(plugin.settings.hasOwnProperty("FOLDER_DECKS"))) {
plugin.settings.FOLDER_DECKS = {}
}
if (!(plugin.settings.hasOwnProperty("FOLDER_TAGS"))) {
plugin.settings.FOLDER_TAGS = {}
}
for (let folder of folder_list) {
let row = main_body.insertRow()

row.insertCell()
row.insertCell()
row.insertCell()

let row_cells = row.children

row_cells[0].innerHTML = folder.path
this.setup_folder_deck(folder, row_cells)
this.setup_folder_tag(folder, row_cells)
}

}

setup_buttons() {
let {containerEl} = this
const plugin = (this as any).plugin
let action_buttons = containerEl.createEl('h3', {text: 'Actions'})
new Setting(action_buttons)
.setName("Regenerate Table")
.setName("Regenerate Note Type Table")
.setDesc("Connect to Anki to regenerate the table with new note types, or get rid of deleted note types.")
.addButton(
button => {
Expand Down Expand Up @@ -304,7 +399,8 @@ export class SettingsTab extends PluginSettingTab {
containerEl.empty()
containerEl.createEl('h2', {text: 'Obsidian_to_Anki settings'})
containerEl.createEl('a', {text: 'For more information check the wiki', href: "https://github.com/Pseudonium/Obsidian_to_Anki/wiki"})
this.setup_table()
this.setup_note_table()
this.setup_folder_table()
this.setup_syntax()
this.setup_defaults()
this.setup_buttons()
Expand Down
Loading