diff --git a/README.md b/README.md index c72bf5b..1fee0fb 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,9 @@ This plugin is the same as the built-in **Word Count** plugin, except when you s - Allows you to store statistics about your vault. - Works with all languages. - Can display a variety of different stats. Including: - - Words, Characters and Sentences in current file. - - Total Words, Characters and Sentences in vault. - - Words, Characters and Sentences typed today. - - Total Files in vault. + - Words, Characters, Sentences, Footnotes, and Pandoc Citations in current file. + - Total Words, Characters, Sentences, Footnotes, Pandoc Citations, and Files in vault. + - Words, Characters, Sentences, Footnotes, and Pandoc Citations typed today. - Highly Customizable status bar that can be adapted to your needs. ## Contributors diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index b58c18a..73e73d2 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -2,6 +2,8 @@ export enum MetricCounter { words, characters, sentences, + footnotes, + citations, pages, files, } diff --git a/src/settings/StatusBarSettings.svelte b/src/settings/StatusBarSettings.svelte index 6031454..14ece51 100644 --- a/src/settings/StatusBarSettings.svelte +++ b/src/settings/StatusBarSettings.svelte @@ -18,6 +18,10 @@ return "Chars in Note" case MetricCounter.sentences: return "Sentences in Note" + case MetricCounter.footnotes: + return "Footnotes in Note" + case MetricCounter.citations: + return "Citations in Note" case MetricCounter.pages: return "Pages in Note" case MetricCounter.files: @@ -31,6 +35,10 @@ return "Daily Chars" case MetricCounter.sentences: return "Daily Sentences" + case MetricCounter.footnotes: + return "Daily Footnotes" + case MetricCounter.citations: + return "Daily Citations" case MetricCounter.pages: return "Daily Pages" case MetricCounter.files: @@ -44,6 +52,10 @@ return "Total Chars" case MetricCounter.sentences: return "Total Sentences" + case MetricCounter.footnotes: + return "Total Footnotes" + case MetricCounter.citations: + return "Total Citations" case MetricCounter.pages: return "Total Pages" case MetricCounter.files: @@ -187,6 +199,8 @@ + + @@ -355,6 +369,8 @@ + + diff --git a/src/stats/Stats.ts b/src/stats/Stats.ts index 38a69aa..b613b58 100644 --- a/src/stats/Stats.ts +++ b/src/stats/Stats.ts @@ -11,15 +11,21 @@ export interface Day { sentences: number; pages: number; files: number; + footnotes: number; + citations: number; totalWords: number; totalCharacters: number; totalSentences: number; + totalFootnotes: number; + totalCitations: number; totalPages: number; } export type ModifiedFiles = Record; export interface FileStat { + footnotes: CountDiff; + citations: CountDiff; words: CountDiff; characters: CountDiff; sentences: CountDiff; diff --git a/src/stats/StatsManager.ts b/src/stats/StatsManager.ts index 59fad1e..0e6b4d6 100644 --- a/src/stats/StatsManager.ts +++ b/src/stats/StatsManager.ts @@ -8,6 +8,8 @@ import { getSentenceCount, getPageCount, getWordCount, + getCitationCount, + getFootnoteCount, } from "../utils/StatUtils"; export default class StatsManager { @@ -80,6 +82,8 @@ export default class StatsManager { const totalWords = await this.calcTotalWords(); const totalCharacters = await this.calcTotalCharacters(); const totalSentences = await this.calcTotalSentences(); + const totalFootnotes = await this.calcTotalFootnotes(); + const totalCitations = await this.calcTotalCitations(); const totalPages = await this.calcTotalPages(); const newDay: Day = { @@ -88,9 +92,13 @@ export default class StatsManager { sentences: 0, pages: 0, files: 0, + footnotes: 0, + citations: 0, totalWords: totalWords, totalCharacters: totalCharacters, totalSentences: totalSentences, + totalFootnotes: totalFootnotes, + totalCitations: totalCitations, totalPages: totalPages, }; @@ -104,6 +112,8 @@ export default class StatsManager { const currentWords = getWordCount(text); const currentCharacters = getCharacterCount(text); const currentSentences = getSentenceCount(text); + const currentCitations = getCitationCount(text); + const currentFootnotes = getFootnoteCount(text); const currentPages = getPageCount(text, this.plugin.settings.pageWords); if ( @@ -119,11 +129,18 @@ export default class StatsManager { currentCharacters - modFiles[fileName].characters.current; this.vaultStats.history[this.today].totalSentences += currentSentences - modFiles[fileName].sentences.current; + this.vaultStats.history[this.today].totalFootnotes += + currentSentences - modFiles[fileName].footnotes.current; + this.vaultStats.history[this.today].totalCitations += + currentSentences - modFiles[fileName].citations.current; this.vaultStats.history[this.today].totalPages += currentPages - modFiles[fileName].pages.current; + modFiles[fileName].words.current = currentWords; modFiles[fileName].characters.current = currentCharacters; modFiles[fileName].sentences.current = currentSentences; + modFiles[fileName].footnotes.current = currentFootnotes; + modFiles[fileName].citations.current = currentCitations; modFiles[fileName].pages.current = currentPages; } else { modFiles[fileName] = { @@ -139,6 +156,14 @@ export default class StatsManager { initial: currentSentences, current: currentSentences, }, + footnotes: { + initial: currentFootnotes, + current: currentFootnotes, + }, + citations: { + initial: currentCitations, + current: currentCitations, + }, pages: { initial: currentPages, current: currentPages, @@ -161,6 +186,16 @@ export default class StatsManager { Math.max(0, counts.sentences.current - counts.sentences.initial) ) .reduce((a, b) => a + b, 0); + + const footnotes = Object.values(modFiles) + .map((counts) => + Math.max(0, counts.footnotes.current - counts.footnotes.initial) + ) + .reduce((a, b) => a + b, 0); + const citations = Object.values(modFiles) + .map((counts) => + Math.max(0, counts.citations.current - counts.citations.initial) + ).reduce((a, b) => a + b, 0); const pages = Object.values(modFiles) .map((counts) => Math.max(0, counts.pages.current - counts.pages.initial) @@ -170,6 +205,8 @@ export default class StatsManager { this.vaultStats.history[this.today].words = words; this.vaultStats.history[this.today].characters = characters; this.vaultStats.history[this.today].sentences = sentences; + this.vaultStats.history[this.today].footnotes = footnotes; + this.vaultStats.history[this.today].citations = citations; this.vaultStats.history[this.today].pages = pages; this.vaultStats.history[this.today].files = this.getTotalFiles(); @@ -189,6 +226,8 @@ export default class StatsManager { todayHist.totalWords = await this.calcTotalWords(); todayHist.totalCharacters = await this.calcTotalCharacters(); todayHist.totalSentences = await this.calcTotalSentences(); + todayHist.totalFootnotes = await this.calcTotalFootnotes(); + todayHist.totalCitations = await this.calcTotalCitations(); todayHist.totalPages = await this.calcTotalPages(); this.update(); } else { @@ -231,7 +270,6 @@ export default class StatsManager { sentence += getSentenceCount(await this.vault.cachedRead(file)); } } - return sentence; } @@ -249,6 +287,30 @@ export default class StatsManager { return pages; } + private async calcTotalFootnotes(): Promise { + let footnotes = 0; + const files = this.vault.getFiles(); + for (const i in files) { + const file = files[i]; + if (file.extension === "md") { + footnotes += getFootnoteCount(await this.vault.cachedRead(file)); + } + } + return footnotes; + } + + private async calcTotalCitations(): Promise { + let citations = 0; + const files = this.vault.getFiles(); + for (const i in files) { + const file = files[i]; + if (file.extension === "md") { + citations += getCitationCount(await this.vault.cachedRead(file)); + } + } + return citations; + } + public getDailyWords(): number { return this.vaultStats.history[this.today].words; } @@ -261,6 +323,14 @@ export default class StatsManager { return this.vaultStats.history[this.today].sentences; } + + public getDailyFootnotes(): number { + return this.vaultStats.history[this.today].footnotes; + } + + public getDailyCitations(): number { + return this.vaultStats.history[this.today].citations; + } public getDailyPages(): number { return this.vaultStats.history[this.today].pages; } @@ -283,6 +353,16 @@ export default class StatsManager { if (!this.vaultStats) return await this.calcTotalSentences(); return this.vaultStats.history[this.today].totalSentences; } + + public async getTotalFootnotes(): Promise { + if (!this.vaultStats) return await this.calcTotalFootnotes(); + return this.vaultStats.history[this.today].totalFootnotes; + } + + public async getTotalCitations(): Promise { + if (!this.vaultStats) return await this.calcTotalCitations(); + return this.vaultStats.history[this.today].totalCitations; + } public async getTotalPages(): Promise { if (!this.vaultStats) return await this.calcTotalPages(); diff --git a/src/status/StatusBar.ts b/src/status/StatusBar.ts index da952c6..79c156d 100644 --- a/src/status/StatusBar.ts +++ b/src/status/StatusBar.ts @@ -4,6 +4,8 @@ import { getWordCount, getCharacterCount, getSentenceCount, + getCitationCount, + getFootnoteCount, getPageCount, } from "src/utils/StatUtils"; import { debounce } from "obsidian"; @@ -108,6 +110,46 @@ export default class StatusBar { : 0)); break; } + } else if (metric.counter === MetricCounter.footnotes) { + switch (metric.type) { + case MetricType.file: + display = display + getFootnoteCount(text); + break; + case MetricType.daily: + display = + display + + (this.plugin.settings.collectStats + ? this.plugin.statsManager.getDailyFootnotes() + : 0); + break; + case MetricType.total: + display = + display + + (await (this.plugin.settings.collectStats + ? this.plugin.statsManager.getTotalFootnotes() + : 0)); + break; + } + } else if (metric.counter === MetricCounter.citations) { + switch (metric.type) { + case MetricType.file: + display = display + getCitationCount(text); + break; + case MetricType.daily: + display = + display + + (this.plugin.settings.collectStats + ? this.plugin.statsManager.getDailyCitations() + : 0); + break; + case MetricType.total: + display = + display + + (await (this.plugin.settings.collectStats + ? this.plugin.statsManager.getTotalCitations() + : 0)); + break; + } } else if (metric.counter === MetricCounter.pages) { switch (metric.type) { case MetricType.file: @@ -230,6 +272,46 @@ export default class StatusBar { : 0)); break; } + } else if (metric.counter === MetricCounter.footnotes) { + switch (metric.type) { + case MetricType.file: + display = display + 0; + break; + case MetricType.daily: + display = + display + + (this.plugin.settings.collectStats + ? this.plugin.statsManager.getDailyFootnotes() + : 0); + break; + case MetricType.total: + display = + display + + (await (this.plugin.settings.collectStats + ? this.plugin.statsManager.getTotalFootnotes() + : 0)); + break; + } + } else if (metric.counter === MetricCounter.citations) { + switch (metric.type) { + case MetricType.file: + display = display + 0; + break; + case MetricType.daily: + display = + display + + (this.plugin.settings.collectStats + ? this.plugin.statsManager.getDailyCitations() + : 0); + break; + case MetricType.total: + display = + display + + (await (this.plugin.settings.collectStats + ? this.plugin.statsManager.getTotalCitations() + : 0)); + break; + } } else if (metric.counter === MetricCounter.pages) { switch (metric.type) { case MetricType.file: diff --git a/src/utils/StatUtils.ts b/src/utils/StatUtils.ts index 2d23966..723f90a 100644 --- a/src/utils/StatUtils.ts +++ b/src/utils/StatUtils.ts @@ -27,6 +27,23 @@ export function getCharacterCount(text: string): number { return text.length; } +export function getFootnoteCount(text: string): number { + const regularFn = text.match(/\[\^\S+](?!:)/g); + const inlineFn = text.match(/\^\[[^^].+?]/g); + + let overallFn = 0; + if (regularFn) overallFn += regularFn.length; + if (inlineFn) overallFn += inlineFn.length; + return overallFn; +} + +export function getCitationCount(text: string): number { + const pandocCitations = text.match(/@[A-Za-z0-9-]+[,;\]](?!\()/gi); + if (!pandocCitations) return 0; + const uniqueCitations = [...new Set(pandocCitations)].length; + return uniqueCitations; +} + export function getSentenceCount(text: string): number { const sentences: number = ( (text || "").match(