Skip to content

Commit

Permalink
Merge pull request #158 from Pseudonium/develop
Browse files Browse the repository at this point in the history
Tag integration and Folder settings
  • Loading branch information
Pseudonium authored Dec 14, 2020
2 parents b22931e + 24cb244 commit 6d1c009
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 28 deletions.
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

0 comments on commit 6d1c009

Please sign in to comment.