Compare commits

..

No commits in common. "master" and "feature/rework-stat-collection" have entirely different histories.

14 changed files with 157 additions and 951 deletions

View file

@ -70,13 +70,3 @@ jobs:
asset_path: ./manifest.json asset_path: ./manifest.json
asset_name: manifest.json asset_name: manifest.json
asset_content_type: application/json asset_content_type: application/json
- name: Upload CSS
id: upload-css
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./src/styles.css
asset_name: styles.css
asset_content_type: text/css

View file

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2022 Luke Leppan Copyright (c) 2021 Luke Leppan
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -1,36 +1,36 @@
# Better Word Count # Better Word Count
![GitHub manifest version](https://img.shields.io/github/manifest-json/v/lukeleppan/better-word-count?color=magenta&label=version&style=for-the-badge) ![GitHub Release Date](https://img.shields.io/github/release-date/lukeleppan/better-word-count?style=for-the-badge) ![Lines of code](https://img.shields.io/tokei/lines/github/lukeleppan/better-word-count?style=for-the-badge) ![Obsidian Downloads](https://img.shields.io/badge/dynamic/json?logo=obsidian&color=%23483699&label=downloads&query=%24%5B%22better-word-count%22%5D.downloads&url=https%3A%2F%2Fraw.githubusercontent.com%2Fobsidianmd%2Fobsidian-releases%2Fmaster%2Fcommunity-plugin-stats.json&style=for-the-badge) ![GitHub Workflow Status](https://img.shields.io/github/workflow/status/lukeleppan/better-word-count/Build%20Release?logo=github&style=for-the-badge) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/lukeleppan/better-word-count?style=for-the-badge) ![Obsidian Downloads](https://img.shields.io/badge/dynamic/json?logo=obsidian&color=%23483699&label=downloads&query=%24%5B%22better-word-count%22%5D.downloads&url=https%3A%2F%2Fraw.githubusercontent.com%2Fobsidianmd%2Fobsidian-releases%2Fmaster%2Fcommunity-plugin-stats.json&style=for-the-badge)
![Better Count Word](https://raw.githubusercontent.com/lukeleppan/better-word-count/master/assets/better-word-count.gif) **IMPORTANT NOTICE:** Due to the introduction of the new Live Preview feature, this plugin needed to be almost entirely rewritten. Currently the plugin only displays the only the word and character count with selecting text features. ALL other features have been scrapped and will be ported in future updates. **DO NOT UPDATE** If you want to use the legacy editor since their is no backwards compatibility yet (I know I'm lazy but I wanted to release the fix, its been too long).
This plugin is the same as the built-in **Word Count** plugin, except when you select text, it will count the selected word instead of the whole document. I recommend turning off the built-in **Word Count** because this plugin is designed to replace that. This plugin also has the ability to store statistics about your vault. This plugin is the same as the built-in **Word Count** plugin, except when you select text, it will count the selected word instead of the whole document. I recommend turning off the built-in **Word Count** because this plugin is designed to replace that. This plugin also has the ability to store statistics about your vault.
![Better Count Word](https://raw.githubusercontent.com/lukeleppan/better-word-count/master/assets/better-word-count.gif)
## Features ## Features
- Allows you to store statistics about your vault. - Allows you to store statistics about your vault.
- Works with all languages. - Works with all languages.
- Can display a variety of different stats. Including: - Can display a variety of different stats. Including:
- Words, Characters, Sentences, Footnotes, and Pandoc Citations in current file. - Words, Characters and Sentences in current file.
- Total Words, Characters, Sentences, Footnotes, Pandoc Citations, and Files in vault. - Total Words, Characters and Sentences in vault.
- Words, Characters, Sentences, Footnotes, and Pandoc Citations typed today. - Words, Characters and Sentences typed today.
- Total Files in vault.
- Highly Customizable status bar that can be adapted to your needs. - Highly Customizable status bar that can be adapted to your needs.
### TODO
- [ ] add statistic view
- [ ] add more statistics (make suggestions)
- [ ] add goals
## Contributors ## Contributors
- @leoccyao - @leoccyao
- Added all word, char, sentence count when not viewing a markdown file. - Added all word, char, sentence count when not viewing a markdown file.
- @chrisgrieser
- Added Footnotes and Citation Counting.
- @bakuzan
- Added page counts.
- Fixed issue that caused errors at start up.
- @THeK3nger
- Fixed issue that occurred when renaming files.
- @lishid - @lishid
- Helped solve the performance issue. - Helped solve the performace issue.
- @Noxellar
- Fixed alt bar spacing issue.
### Special Thanks ### Special Thanks
@ -44,9 +44,3 @@ This plugin is the same as the built-in **Word Count** plugin, except when you s
- @lammersma - @lammersma
- @aknighty74 - @aknighty74
- @dhruvik7 - @dhruvik7
### Support Me
If you enjoyed the plugin you can support me by clicking the button below. I am currently a student so anything helps.
[<img src="https://cdn.buymeacoffee.com/buttons/v2/default-violet.png" alt="BuyMeACoffee" width="100">](https://www.buymeacoffee.com/lukeleppan)

View file

@ -1,7 +1,7 @@
{ {
"id": "better-word-count", "id": "better-word-count",
"name": "Better Word Count", "name": "Better Word Count",
"version": "0.9.6", "version": "0.8.1",
"description": "Counts the words of selected text in the editor.", "description": "Counts the words of selected text in the editor.",
"author": "Luke Leppan", "author": "Luke Leppan",
"authorUrl": "https://lukeleppan.com", "authorUrl": "https://lukeleppan.com",

View file

@ -1,6 +1,6 @@
{ {
"name": "better-word-count", "name": "better-word-count",
"version": "0.9.6", "version": "0.8.1",
"description": "Counts the words of selected text in the editor.", "description": "Counts the words of selected text in the editor.",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {

View file

@ -1,4 +1,3 @@
import { Transaction } from "@codemirror/state";
import { import {
ViewUpdate, ViewUpdate,
PluginValue, PluginValue,
@ -23,19 +22,9 @@ class EditorPlugin implements PluginValue {
} }
const tr = update.transactions[0]; const tr = update.transactions[0];
if (!tr) return;
if (!tr) {
return;
}
// When selecting text with Shift+Home the userEventType is undefined.
// This is probably a bug in codemirror, for the time being doing an explict check
// for the type allows us to update the stats for the selection.
const userEventTypeUndefined =
tr.annotation(Transaction.userEvent) === undefined;
if ( if (
(tr.isUserEvent("select") || userEventTypeUndefined) && tr.isUserEvent("select") &&
tr.newSelection.ranges[0].from !== tr.newSelection.ranges[0].to tr.newSelection.ranges[0].from !== tr.newSelection.ranges[0].to
) { ) {
let text = ""; let text = "";

View file

@ -22,18 +22,18 @@ export default class BetterWordCount extends Plugin {
async onload() { async onload() {
// Settings Store // Settings Store
// this.register( this.register(
// settingsStore.subscribe((value) => { settingsStore.subscribe((value) => {
// this.settings = value; this.settings = value;
// }) })
// ); );
// Handle Settings // Handle Settings
this.settings = Object.assign(DEFAULT_SETTINGS, await this.loadData()); this.settings = Object.assign(DEFAULT_SETTINGS, await this.loadData());
this.addSettingTab(new BetterWordCountSettingsTab(this.app, this)); this.addSettingTab(new BetterWordCountSettingsTab(this.app, this));
// Handle Statistics // Handle Statistics
if (this.settings.collectStats) { if (this.settings.collectStats) {
this.statsManager = new StatsManager(this.app.vault, this.app.workspace, this); this.statsManager = new StatsManager(this.app.vault, this.app.workspace);
} }
// Handle Status Bar // Handle Status Bar
@ -51,20 +51,14 @@ export default class BetterWordCount extends Plugin {
this.app.workspace.on( this.app.workspace.on(
"active-leaf-change", "active-leaf-change",
async (leaf: WorkspaceLeaf) => { async (leaf: WorkspaceLeaf) => {
this.giveEditorPlugin(leaf);
if (leaf.view.getViewType() !== "markdown") {
this.statusBar.updateAltBar();
}
if (!this.settings.collectStats) return;
await this.statsManager.recalcTotals(); await this.statsManager.recalcTotals();
this.giveEditorPlugin(leaf);
} }
) )
); );
this.registerEvent( this.registerEvent(
this.app.vault.on("delete", async () => { this.app.vault.on("delete", async () => {
if (!this.settings.collectStats) return;
await this.statsManager.recalcTotals(); await this.statsManager.recalcTotals();
}) })
); );
@ -72,7 +66,7 @@ export default class BetterWordCount extends Plugin {
giveEditorPlugin(leaf: WorkspaceLeaf): void { giveEditorPlugin(leaf: WorkspaceLeaf): void {
//@ts-expect-error, not typed //@ts-expect-error, not typed
const editor = leaf?.view?.editor; const editor = leaf.view.editor;
if (editor) { if (editor) {
const editorView = editor.cm as EditorView; const editorView = editor.cm as EditorView;
const editorPlug = editorView.plugin(editorPlugin); const editorPlug = editorView.plugin(editorPlugin);

View file

@ -1,79 +1,44 @@
export enum MetricCounter { export enum Counter {
words, fileWords,
characters, fileChars,
sentences, fileSentences,
footnotes, totalWords,
citations, totalChars,
pages, totalSentences,
files, totalNotes,
}
export enum MetricType {
file,
daily,
total,
folder,
}
export interface Metric {
type: MetricType;
counter: MetricCounter;
folder?: string;
} }
export interface StatusBarItem { export interface StatusBarItem {
prefix: string; prefix: string;
suffix: string; suffix: string;
metric: Metric; count: Counter;
} }
export const BLANK_SB_ITEM: StatusBarItem = { export const BLANK_SB_ITEM: StatusBarItem = {
prefix: "", prefix: "",
suffix: "", suffix: "",
metric: { count: null,
type: null,
counter: null,
},
}; };
export interface BetterWordCountSettings { export interface BetterWordCountSettings {
statusBar: StatusBarItem[]; statusBar: StatusBarItem[];
altBar: StatusBarItem[];
countComments: boolean; countComments: boolean;
collectStats: boolean; collectStats: boolean;
pageWords: number;
} }
export const DEFAULT_SETTINGS: BetterWordCountSettings = { export const DEFAULT_SETTINGS: BetterWordCountSettings = Object.freeze({
statusBar: [ statusBar: [
{ {
prefix: "", prefix: "",
suffix: " words", suffix: " words",
metric: { count: Counter.fileWords,
type: MetricType.file,
counter: MetricCounter.words,
},
}, },
{ {
prefix: " ", prefix: " ",
suffix: " characters", suffix: " characters",
metric: { count: Counter.fileChars,
type: MetricType.file,
counter: MetricCounter.characters,
},
},
],
altBar: [
{
prefix: "",
suffix: " files",
metric: {
type: MetricType.total,
counter: MetricCounter.files,
},
}, },
], ],
countComments: false, countComments: false,
collectStats: false, collectStats: false,
pageWords: 300, });
};

View file

@ -1,4 +1,4 @@
import { App, PluginSettingTab, Setting, ToggleComponent, TextComponent } from "obsidian"; import { App, PluginSettingTab, Setting, ToggleComponent } from "obsidian";
import type BetterWordCount from "src/main"; import type BetterWordCount from "src/main";
import { addStatusBarSettings } from "./StatusBarSettings"; import { addStatusBarSettings } from "./StatusBarSettings";
@ -18,7 +18,7 @@ export default class BetterWordCountSettingsTab extends PluginSettingTab {
new Setting(containerEl) new Setting(containerEl)
.setName("Collect Statistics") .setName("Collect Statistics")
.setDesc( .setDesc(
"Reload Required for change to take effect. Turn on to start collecting daily statistics of your writing. Stored in the vault-stats.json file in the .obsidian of your vault. This is required for counts of the day as well as total counts." "Reload Required for change to take effect. Turn on to start collecting daily statistics of your writing. Stored in the .vault-stats file in the root of your vault. This is required for counts of the day."
) )
.addToggle((cb: ToggleComponent) => { .addToggle((cb: ToggleComponent) => {
cb.setValue(this.plugin.settings.collectStats); cb.setValue(this.plugin.settings.collectStats);
@ -37,20 +37,15 @@ export default class BetterWordCountSettingsTab extends PluginSettingTab {
await this.plugin.saveSettings(); await this.plugin.saveSettings();
}); });
}); });
new Setting(containerEl)
.setName("Page Word Count")
.setDesc("Set how many words count as one \"page\"")
.addText((text: TextComponent) => {
text.inputEl.type = "number";
text.setPlaceholder("300");
text.setValue(this.plugin.settings.pageWords.toString());
text.onChange(async (value: string) => {
this.plugin.settings.pageWords = parseInt(value);
await this.plugin.saveSettings();
});
});
// Status Bar Settings // Status Bar Settings
containerEl
.createEl("h4", { text: "Status Bar Settings" })
.addClass("bwc-status-bar-settings-title");
containerEl.createEl("p", {
text: "Here you can customize what statistics are displayed on the status bar.",
});
addStatusBarSettings(this.plugin, containerEl); addStatusBarSettings(this.plugin, containerEl);
} }
} }

View file

@ -1,68 +1,38 @@
<script lang="ts"> <script lang="ts">
import type { StatusBarItem } from "./Settings"; import type { StatusBarItem } from "./Settings";
import { MetricType, MetricCounter, BLANK_SB_ITEM, DEFAULT_SETTINGS } from "./Settings"; import { Counter, BLANK_SB_ITEM } from "./Settings";
import type BetterWordCount from "src/main"; import type BetterWordCount from "src/main";
export let plugin: BetterWordCount; export let plugin: BetterWordCount;
const { settings } = plugin; const { settings } = plugin;
let statusItems: StatusBarItem[] = [...plugin.settings.statusBar]; let statusItems: StatusBarItem[] = [...plugin.settings.statusBar];
let altSItems: StatusBarItem[] = [...plugin.settings.altBar];
function metricToString(metric: Metric): string { function counterToString(count: Counter): string {
if (metric.type === MetricType.file) { switch (count) {
switch (metric.counter) { case Counter.fileWords:
case MetricCounter.words: return "File Words";
return "Words in Note"
case MetricCounter.characters: case Counter.fileChars:
return "Chars in Note" return "File Chars";
case MetricCounter.sentences:
return "Sentences in Note" case Counter.fileSentences:
case MetricCounter.footnotes: return "File Sentences";
return "Footnotes in Note"
case MetricCounter.citations: case Counter.totalWords:
return "Citations in Note" return "Total Words";
case MetricCounter.pages:
return "Pages in Note" case Counter.totalChars:
case MetricCounter.files: return "Total Chars";
return "Total Notes"
} case Counter.totalSentences:
} else if (metric.type === MetricType.daily) { return "Total Sentences"
switch (metric.counter) {
case MetricCounter.words: case Counter.totalNotes:
return "Daily Words" return "Total Notes";
case MetricCounter.characters:
return "Daily Chars" default:
case MetricCounter.sentences: return "Select Options"
return "Daily Sentences"
case MetricCounter.footnotes:
return "Daily Footnotes"
case MetricCounter.citations:
return "Daily Citations"
case MetricCounter.pages:
return "Daily Pages"
case MetricCounter.files:
return "Total Notes"
}
} else if (metric.type === MetricType.total) {
switch (metric.counter) {
case MetricCounter.words:
return "Total Words"
case MetricCounter.characters:
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:
return "Total Notes"
}
} else {
return "Select Options"
} }
} }
@ -77,32 +47,19 @@
async function update(statusItems: StatusBarItem[]) { async function update(statusItems: StatusBarItem[]) {
plugin.settings.statusBar = statusItems.filter((item) => { plugin.settings.statusBar = statusItems.filter((item) => {
if (metricToString(item.metric) !== "Select Options") { if (counterToString(item.count) !== "Select Options") {
return item; return item;
} }
}); });
await plugin.saveSettings();
}
async function updateAlt(altSItems: StatusBarItem[]) {
plugin.settings.altBar = altSItems.filter((item) => {
if (metricToString(item.metric) !== "Select Options") {
return item;
}
});
await plugin.saveSettings(); await plugin.saveSettings();
} }
</script> </script>
<div> <div>
<h4>Markdown Status Bar</h4>
<p>Here you can customize what statistics are displayed on the status bar when editing a markdown note.</p>
<div class="bwc-sb-buttons"> <div class="bwc-sb-buttons">
<button <button
aria-label="Add New Status Bar Item" aria-label="Add New Status Bar Item"
on:click={async () => (statusItems = [...statusItems, JSON.parse(JSON.stringify(BLANK_SB_ITEM))])} on:click={async () => (statusItems = [...statusItems, Object.create(BLANK_SB_ITEM)])}
> >
<div class="icon"> <div class="icon">
Add Item Add Item
@ -111,26 +68,19 @@
<button <button
aria-label="Reset Status Bar to Default" aria-label="Reset Status Bar to Default"
on:click={async () => { on:click={async () => {
statusItems = [ statusItems = [{
{ prefix: "",
prefix: "", suffix: " words",
suffix: " words", count: Counter.fileWords,
metric: { },
type: MetricType.file, {
counter: MetricCounter.words, prefix: " ",
}, suffix: " characters",
}, count: Counter.fileChars,
{ },
prefix: " ", ];
suffix: " characters", await update(statusItems);
metric: { }}
type: MetricType.file,
counter: MetricCounter.characters,
},
},
];
await update(statusItems);
}}
> >
<div class="icon"> <div class="icon">
Reset Reset
@ -141,7 +91,7 @@
<details class="bwc-sb-item-setting"> <details class="bwc-sb-item-setting">
<summary> <summary>
<span class="bwc-sb-item-text"> <span class="bwc-sb-item-text">
{metricToString(item.metric)} {counterToString(item.count)}
</span> </span>
<span class="bwc-sb-buttons"> <span class="bwc-sb-buttons">
{#if i !== 0} {#if i !== 0}
@ -179,55 +129,39 @@
</summary> </summary>
<div class="setting-item"> <div class="setting-item">
<div class="setting-item-info"> <div class="setting-item-info">
<div class="setting-item-name">Metric Counter</div> <div class="setting-item-name">Count Type</div>
<div class="setting-item-description"> <div class="setting-item-description">
Select the counter to display, e.g. words, characters. This is the type of counter that will be displayed.
</div> </div>
</div> </div>
<div class="setting-item-control"> <div class="setting-item-control">
<select <select
class="dropdown" class="dropdown"
value={item.metric.counter} value={item.count}
on:change={async (e) => { on:change={async (e) => {
const {value} = e.target; const {value} = e.target;
item.metric.counter = MetricCounter[MetricCounter[value]]; item.count = Counter[Counter[value]];
await update(statusItems); await update(statusItems);
await plugin.saveSettings(); await plugin.saveSettings();
}} }}
> >
<option value>Select Option</option> <option value>Select Option</option>
<option value={MetricCounter.words}>Words</option> <optgroup label="Words">
<option value={MetricCounter.characters}>Characters</option> <option value={Counter.fileWords}>{counterToString(Counter.fileWords)}</option>
<option value={MetricCounter.sentences}>Sentences</option> <option value={Counter.totalWords}>{counterToString(Counter.totalWords)}</option>
<option value={MetricCounter.footnotes}>Footnotes</option> </optgroup>
<option value={MetricCounter.citations}>Citations</option> <optgroup label="Characters">
<option value={MetricCounter.pages}>Pages</option> <option value={Counter.fileChars}>{counterToString(Counter.fileChars)}</option>
<option value={MetricCounter.files}>Files</option> <option value={Counter.totalChars}>{counterToString(Counter.totalChars)}</option>
</select> </optgroup>
</div> <optgroup label="Sentences">
</div> <option value={Counter.fileSentences}>{counterToString(Counter.fileSentences)}</option>
<div class="setting-item"> <option value={Counter.totalSentences}>{counterToString(Counter.totalSentences)}</option>
<div class="setting-item-info"> </optgroup>
<div class="setting-item-name">Metric Type</div> <optgroup label="Notes">
<div class="setting-item-description"> <option value={Counter.totalNotes}>{counterToString(Counter.totalNotes)}</option>
Select the type of metric that you want displayed. </optgroup>
</div>
</div>
<div class="setting-item-control">
<select
class="dropdown"
value={item.metric.type}
on:change={async (e) => {
const {value} = e.target;
item.metric.type = MetricType[MetricType[value]];
await update(statusItems);
await plugin.saveSettings();
}}
>
<option value>Select Option</option>
<option value={MetricType.file}>Current Note</option>
<option value={MetricType.daily}>Daily Metric</option>
<option value={MetricType.total}>Total in Vault</option>
</select> </select>
</div> </div>
</div> </div>
@ -275,174 +209,4 @@
</div> </div>
</details> </details>
{/each} {/each}
<h4>Alternative Status Bar</h4>
<p>Here you can customize what statistics are displayed on the status bar when not editing a markdown file.</p>
<div class="bwc-sb-buttons">
<button
aria-label="Add New Status Bar Item"
on:click={async () => (altSItems = [...altSItems, JSON.parse(JSON.stringify(BLANK_SB_ITEM))])}
>
<div class="icon">
Add Item
</div>
</button>
<button
aria-label="Reset Status Bar to Default"
on:click={async () => {
altSItems = [
{
prefix: "",
suffix: " files",
metric: {
type: MetricType.total,
counter: MetricCounter.files,
},
},
];
await update(statusItems);
}}
>
<div class="icon">
Reset
</div>
</button>
</div>
{#each altSItems as item, i}
<details class="bwc-sb-item-setting">
<summary>
<span class="bwc-sb-item-text">
{metricToString(item.metric)}
</span>
<span class="bwc-sb-buttons">
{#if i !== 0}
<button
aria-label="Move Status Bar Item Up"
on:click={async () => {
altSItems = swapStatusBarItems(i, i-1, altSItems);
await updateAlt(altSItems);
}}
>
</button>
{/if}
{#if i !== altSItems.length - 1}
<button
aria-label="Move Status Bar Item Down"
on:click={async () => {
altSItems = swapStatusBarItems(i, i+1, altSItems);
await updateAlt(altSItems);
}}
>
</button>
{/if}
<button
aria-label="Remove Status Bar Item"
on:click={async () => {
altSItems = altSItems.filter((item, j) => i !== j);
await updateAlt(altSItems);
}}
>
X
</button>
</span>
</summary>
<div class="setting-item">
<div class="setting-item-info">
<div class="setting-item-name">Metric Counter</div>
<div class="setting-item-description">
Select the counter to display, e.g. words, characters.
</div>
</div>
<div class="setting-item-control">
<select
class="dropdown"
value={item.metric.counter}
on:change={async (e) => {
const {value} = e.target;
item.metric.counter = MetricCounter[MetricCounter[value]];
await updateAlt(altSItems);
await plugin.saveSettings();
}}
>
<option value>Select Option</option>
<option value={MetricCounter.words}>Words</option>
<option value={MetricCounter.characters}>Characters</option>
<option value={MetricCounter.sentences}>Sentences</option>
<option value={MetricCounter.footnotes}>Footnotes</option>
<option value={MetricCounter.citations}>Citations</option>
<option value={MetricCounter.pages}>Pages</option>
<option value={MetricCounter.files}>Files</option>
</select>
</div>
</div>
<div class="setting-item">
<div class="setting-item-info">
<div class="setting-item-name">Metric Type</div>
<div class="setting-item-description">
Select the type of metric that you want displayed.
</div>
</div>
<div class="setting-item-control">
<select
class="dropdown"
value={item.metric.type}
on:change={async (e) => {
const {value} = e.target;
item.metric.type = MetricType[MetricType[value]];
await updateAlt(altSItems);
await plugin.saveSettings();
}}
>
<option value>Select Option</option>
<option value={MetricType.file}>Current Note</option>
<option value={MetricType.daily}>Daily Metric</option>
<option value={MetricType.total}>Total in Vault</option>
</select>
</div>
</div>
<div class="setting-item">
<div class="setting-item-info">
<div class="setting-item-name">Prefix Text</div>
<div class="setting-item-description">
This is the text that is placed before the count.
</div>
</div>
<div class="setting-item-control">
<input
type="text"
name="prefix"
value={item.prefix}
on:change={async (e) => {
const { value } = e.target;
item.prefix = value;
await updateAlt(altSItems);
await plugin.saveSettings();
}}
/>
</div>
</div>
<div class="setting-item">
<div class="setting-item-info">
<div class="setting-item-name">Suffix Text</div>
<div class="setting-item-description">
This is the text that is placed after the count.
</div>
</div>
<div class="setting-item-control">
<input
type="text"
name="suffix"
value={item.suffix}
on:change={async (e) => {
const { value } = e.target;
item.suffix = value;
await updateAlt(altSItems);
await plugin.saveSettings();
}}
/>
</div>
</div>
</details>
{/each}
</div> </div>

View file

@ -9,27 +9,18 @@ export interface Day {
words: number; words: number;
characters: number; characters: number;
sentences: number; sentences: number;
pages: number;
files: number; files: number;
footnotes: number;
citations: number;
totalWords: number; totalWords: number;
totalCharacters: number; totalCharacters: number;
totalSentences: number; totalSentences: number;
totalFootnotes: number;
totalCitations: number;
totalPages: number;
} }
export type ModifiedFiles = Record<string, FileStat>; export type ModifiedFiles = Record<string, FileStat>;
export interface FileStat { export interface FileStat {
footnotes: CountDiff;
citations: CountDiff;
words: CountDiff; words: CountDiff;
characters: CountDiff; characters: CountDiff;
sentences: CountDiff; sentences: CountDiff;
pages: CountDiff;
} }
export interface CountDiff { export interface CountDiff {

View file

@ -1,49 +1,27 @@
import { debounce, Debouncer, TFile, Vault, Workspace } from "obsidian"; import { debounce, Debouncer, TFile, Vault, Workspace } from "obsidian";
import type BetterWordCount from "../main";
import { STATS_FILE } from "../constants"; import { STATS_FILE } from "../constants";
import type { Day, VaultStatistics } from "./Stats"; import type {
Day,
VaultStatistics,
} from "./Stats";
import moment from "moment"; import moment from "moment";
import { import {
getCharacterCount, getCharacterCount,
getSentenceCount, getSentenceCount,
getPageCount,
getWordCount, getWordCount,
getCitationCount,
getFootnoteCount,
cleanComments,
} from "../utils/StatUtils"; } from "../utils/StatUtils";
export default class StatsManager { export default class StatsManager {
private vault: Vault; private vault: Vault;
private workspace: Workspace; private workspace: Workspace;
private plugin: BetterWordCount;
private vaultStats: VaultStatistics; private vaultStats: VaultStatistics;
private today: string; private today: string;
public debounceChange; public debounceChange;
constructor(vault: Vault, workspace: Workspace, plugin: BetterWordCount) { constructor(vault: Vault, workspace: Workspace) {
this.vault = vault; this.vault = vault;
this.workspace = workspace; this.workspace = workspace;
this.plugin = plugin; this.debounceChange = debounce((text:string) => this.change(text), 50, false)
this.debounceChange = debounce(
(text: string) => this.change(text),
50,
false
);
this.vault.on("rename", (new_name, old_path) => {
if (this.vaultStats.modifiedFiles.hasOwnProperty(old_path)) {
const content = this.vaultStats.modifiedFiles[old_path];
delete this.vaultStats.modifiedFiles[old_path];
this.vaultStats.modifiedFiles[new_name.path] = content;
}
});
this.vault.on("delete", (deleted_file) => {
if (this.vaultStats.modifiedFiles.hasOwnProperty(deleted_file.path)) {
delete this.vaultStats.modifiedFiles[deleted_file.path];
}
});
this.vault.adapter.exists(STATS_FILE).then(async (exists) => { this.vault.adapter.exists(STATS_FILE).then(async (exists) => {
if (!exists) { if (!exists) {
@ -55,14 +33,6 @@ export default class StatsManager {
this.vaultStats = JSON.parse(await this.vault.adapter.read(STATS_FILE)); this.vaultStats = JSON.parse(await this.vault.adapter.read(STATS_FILE));
} else { } else {
this.vaultStats = JSON.parse(await this.vault.adapter.read(STATS_FILE)); this.vaultStats = JSON.parse(await this.vault.adapter.read(STATS_FILE));
if (!this.vaultStats.hasOwnProperty("history")) {
const vaultSt: VaultStatistics = {
history: {},
modifiedFiles: {},
};
await this.vault.adapter.write(STATS_FILE, JSON.stringify(vaultSt));
}
this.vaultStats = JSON.parse(await this.vault.adapter.read(STATS_FILE));
} }
await this.updateToday(); await this.updateToday();
@ -83,24 +53,15 @@ export default class StatsManager {
const totalWords = await this.calcTotalWords(); const totalWords = await this.calcTotalWords();
const totalCharacters = await this.calcTotalCharacters(); const totalCharacters = await this.calcTotalCharacters();
const totalSentences = await this.calcTotalSentences(); const totalSentences = await this.calcTotalSentences();
const totalFootnotes = await this.calcTotalFootnotes();
const totalCitations = await this.calcTotalCitations();
const totalPages = await this.calcTotalPages();
const newDay: Day = { const newDay: Day = {
words: 0, words: 0,
characters: 0, characters: 0,
sentences: 0, sentences: 0,
pages: 0,
files: 0, files: 0,
footnotes: 0,
citations: 0,
totalWords: totalWords, totalWords: totalWords,
totalCharacters: totalCharacters, totalCharacters: totalCharacters,
totalSentences: totalSentences, totalSentences: totalSentences,
totalFootnotes: totalFootnotes,
totalCitations: totalCitations,
totalPages: totalPages,
}; };
this.vaultStats.modifiedFiles = {}; this.vaultStats.modifiedFiles = {};
@ -109,17 +70,10 @@ export default class StatsManager {
} }
public async change(text: string) { public async change(text: string) {
if (this.plugin.settings.countComments) {
text = cleanComments(text);
}
const fileName = this.workspace.getActiveFile().path; const fileName = this.workspace.getActiveFile().path;
const currentWords = getWordCount(text); const currentWords = getWordCount(text);
const currentCharacters = getCharacterCount(text); const currentCharacters = getCharacterCount(text);
const currentSentences = getSentenceCount(text); const currentSentences = getSentenceCount(text);
const currentCitations = getCitationCount(text);
const currentFootnotes = getFootnoteCount(text);
const currentPages = getPageCount(text, this.plugin.settings.pageWords);
if ( if (
this.vaultStats.history.hasOwnProperty(this.today) && this.vaultStats.history.hasOwnProperty(this.today) &&
this.today === moment().format("YYYY-MM-DD") this.today === moment().format("YYYY-MM-DD")
@ -127,25 +81,14 @@ export default class StatsManager {
let modFiles = this.vaultStats.modifiedFiles; let modFiles = this.vaultStats.modifiedFiles;
if (modFiles.hasOwnProperty(fileName)) { if (modFiles.hasOwnProperty(fileName)) {
this.vaultStats.history[this.today].totalWords += this.vaultStats.history[this.today].totalWords += currentWords - modFiles[fileName].words.current;
currentWords - modFiles[fileName].words.current; this.vaultStats.history[this.today].totalCharacters += currentCharacters - modFiles[fileName].characters.current;
this.vaultStats.history[this.today].totalCharacters += this.vaultStats.history[this.today].totalSentences += currentSentences - modFiles[fileName].sentences.current;
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].words.current = currentWords;
modFiles[fileName].characters.current = currentCharacters; modFiles[fileName].characters.current = currentCharacters;
modFiles[fileName].sentences.current = currentSentences; modFiles[fileName].sentences.current = currentSentences;
modFiles[fileName].footnotes.current = currentFootnotes;
modFiles[fileName].citations.current = currentCitations;
modFiles[fileName].pages.current = currentPages;
} else { } else {
modFiles[fileName] = { modFiles[fileName] = {
words: { words: {
@ -159,19 +102,7 @@ export default class StatsManager {
sentences: { sentences: {
initial: currentSentences, initial: currentSentences,
current: currentSentences, current: currentSentences,
}, }
footnotes: {
initial: currentFootnotes,
current: currentFootnotes,
},
citations: {
initial: currentCitations,
current: currentCitations,
},
pages: {
initial: currentPages,
current: currentPages,
},
}; };
} }
@ -191,27 +122,9 @@ export default class StatsManager {
) )
.reduce((a, b) => a + b, 0); .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)
)
.reduce((a, b) => a + b, 0);
this.vaultStats.history[this.today].words = words; this.vaultStats.history[this.today].words = words;
this.vaultStats.history[this.today].characters = characters; this.vaultStats.history[this.today].characters = characters;
this.vaultStats.history[this.today].sentences = sentences; 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(); this.vaultStats.history[this.today].files = this.getTotalFiles();
await this.update(); await this.update();
@ -228,11 +141,8 @@ export default class StatsManager {
) { ) {
const todayHist: Day = this.vaultStats.history[this.today]; const todayHist: Day = this.vaultStats.history[this.today];
todayHist.totalWords = await this.calcTotalWords(); todayHist.totalWords = await this.calcTotalWords();
todayHist.totalCharacters = await this.calcTotalCharacters(); todayHist.totalCharacters = await this.calcTotalCharacters()
todayHist.totalSentences = await this.calcTotalSentences(); todayHist.totalSentences = await this.calcTotalSentences()
todayHist.totalFootnotes = await this.calcTotalFootnotes();
todayHist.totalCitations = await this.calcTotalCitations();
todayHist.totalPages = await this.calcTotalPages();
this.update(); this.update();
} else { } else {
this.updateToday(); this.updateToday();
@ -274,71 +184,10 @@ export default class StatsManager {
sentence += getSentenceCount(await this.vault.cachedRead(file)); sentence += getSentenceCount(await this.vault.cachedRead(file));
} }
} }
return sentence; return sentence;
} }
private async calcTotalPages(): Promise<number> {
let pages = 0;
const files = this.vault.getFiles();
for (const i in files) {
const file = files[i];
if (file.extension === "md") {
pages += getPageCount(await this.vault.cachedRead(file), this.plugin.settings.pageWords);
}
}
return pages;
}
private async calcTotalFootnotes(): Promise<number> {
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<number> {
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;
}
public getDailyCharacters(): number {
return this.vaultStats.history[this.today].characters;
}
public getDailySentences(): number {
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;
}
public getTotalFiles(): number { public getTotalFiles(): number {
return this.vault.getMarkdownFiles().length; return this.vault.getMarkdownFiles().length;
} }
@ -357,19 +206,4 @@ export default class StatsManager {
if (!this.vaultStats) return await this.calcTotalSentences(); if (!this.vaultStats) return await this.calcTotalSentences();
return this.vaultStats.history[this.today].totalSentences; return this.vaultStats.history[this.today].totalSentences;
} }
public async getTotalFootnotes(): Promise<number> {
if (!this.vaultStats) return await this.calcTotalFootnotes();
return this.vaultStats.history[this.today].totalFootnotes;
}
public async getTotalCitations(): Promise<number> {
if (!this.vaultStats) return await this.calcTotalCitations();
return this.vaultStats.history[this.today].totalCitations;
}
public async getTotalPages(): Promise<number> {
if (!this.vaultStats) return await this.calcTotalPages();
return this.vaultStats.history[this.today].totalPages;
}
} }

View file

@ -1,13 +1,9 @@
import { MetricCounter, MetricType } from "src/settings/Settings"; import { Counter } from "src/settings/Settings";
import type BetterWordCount from "../main"; import type BetterWordCount from "../main";
import { import {
getWordCount, getWordCount,
getCharacterCount, getCharacterCount,
getSentenceCount, getSentenceCount,
getCitationCount,
getFootnoteCount,
getPageCount,
cleanComments,
} from "src/utils/StatUtils"; } from "src/utils/StatUtils";
import { debounce } from "obsidian"; import { debounce } from "obsidian";
@ -26,7 +22,7 @@ export default class StatusBar {
); );
this.statusBarEl.classList.add("mod-clickable"); this.statusBarEl.classList.add("mod-clickable");
this.statusBarEl.setAttribute("aria-label", "!!!"); this.statusBarEl.setAttribute("aria-label", "Coming Soon");
this.statusBarEl.setAttribute("aria-label-position", "top"); this.statusBarEl.setAttribute("aria-label-position", "top");
this.statusBarEl.addEventListener("click", (ev: MouseEvent) => this.statusBarEl.addEventListener("click", (ev: MouseEvent) =>
this.onClick(ev) this.onClick(ev)
@ -45,322 +41,37 @@ export default class StatusBar {
const sb = this.plugin.settings.statusBar; const sb = this.plugin.settings.statusBar;
let display = ""; let display = "";
if (!this.plugin.settings.countComments) {
text = cleanComments(text);
}
for (let i = 0; i < sb.length; i++) { for (let i = 0; i < sb.length; i++) {
const sbItem = sb[i]; const sbItem = sb[i];
display = display + sbItem.prefix; display = display + sbItem.prefix;
const metric = sbItem.metric; switch (sbItem.count) {
case Counter.fileWords:
display = display + getWordCount(text);
break;
case Counter.fileChars:
display = display + getCharacterCount(text);
break;
case Counter.fileSentences:
display = display + getSentenceCount(text);
break;
case Counter.totalWords:
display = display + (await this.plugin.statsManager.getTotalWords());
break;
case Counter.totalChars:
display =
display + (await this.plugin.statsManager.getTotalCharacters());
break;
case Counter.totalSentences:
display =
display + (await this.plugin.statsManager.getTotalSentences());
break;
case Counter.totalNotes:
display = display + this.plugin.statsManager.getTotalFiles();
break;
if (metric.counter === MetricCounter.words) { default:
switch (metric.type) { break;
case MetricType.file:
display = display + getWordCount(text);
break;
case MetricType.daily:
display =
display +
(this.plugin.settings.collectStats
? this.plugin.statsManager.getDailyWords()
: 0);
break;
case MetricType.total:
display =
display +
(await (this.plugin.settings.collectStats
? this.plugin.statsManager.getTotalWords()
: 0));
break;
}
} else if (metric.counter === MetricCounter.characters) {
switch (metric.type) {
case MetricType.file:
display = display + getCharacterCount(text);
break;
case MetricType.daily:
display =
display +
(this.plugin.settings.collectStats
? this.plugin.statsManager.getDailyCharacters()
: 0);
break;
case MetricType.total:
display =
display +
(await (this.plugin.settings.collectStats
? this.plugin.statsManager.getTotalCharacters()
: 0));
break;
}
} else if (metric.counter === MetricCounter.sentences) {
switch (metric.type) {
case MetricType.file:
display = display + getSentenceCount(text);
break;
case MetricType.daily:
display =
display +
(this.plugin.settings.collectStats
? this.plugin.statsManager.getDailySentences()
: 0);
break;
case MetricType.total:
display =
display +
(await (this.plugin.settings.collectStats
? this.plugin.statsManager.getTotalSentences()
: 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:
display = display + getPageCount(text, this.plugin.settings.pageWords);
break;
case MetricType.daily:
display =
display +
(this.plugin.settings.collectStats
? this.plugin.statsManager.getDailyPages()
: 0);
break;
case MetricType.total:
display =
display +
(await (this.plugin.settings.collectStats
? this.plugin.statsManager.getTotalPages()
: 0));
break;
}
} else if (metric.counter === MetricCounter.files) {
switch (metric.type) {
case MetricType.file:
display =
display +
(this.plugin.settings.collectStats
? this.plugin.statsManager.getTotalFiles()
: 0);
break;
case MetricType.daily:
display =
display +
(this.plugin.settings.collectStats
? this.plugin.statsManager.getTotalFiles()
: 0);
break;
case MetricType.total:
display =
display +
(this.plugin.settings.collectStats
? this.plugin.statsManager.getTotalFiles()
: 0);
break;
}
}
display = display + sbItem.suffix;
}
this.displayText(display);
}
async updateAltBar() {
const ab = this.plugin.settings.altBar;
let display = "";
for (let i = 0; i < ab.length; i++) {
const sbItem = ab[i];
display = display + sbItem.prefix;
const metric = sbItem.metric;
if (metric.counter === MetricCounter.words) {
switch (metric.type) {
case MetricType.file:
display = display + 0;
break;
case MetricType.daily:
display =
display +
(this.plugin.settings.collectStats
? this.plugin.statsManager.getDailyWords()
: 0);
break;
case MetricType.total:
display =
display +
(await (this.plugin.settings.collectStats
? this.plugin.statsManager.getTotalWords()
: 0));
break;
}
} else if (metric.counter === MetricCounter.characters) {
switch (metric.type) {
case MetricType.file:
display = display + 0;
break;
case MetricType.daily:
display =
display +
(this.plugin.settings.collectStats
? this.plugin.statsManager.getDailyCharacters()
: 0);
break;
case MetricType.total:
display =
display +
(await (this.plugin.settings.collectStats
? this.plugin.statsManager.getTotalCharacters()
: 0));
break;
}
} else if (metric.counter === MetricCounter.sentences) {
switch (metric.type) {
case MetricType.file:
display = display + 0;
break;
case MetricType.daily:
display =
display +
(this.plugin.settings.collectStats
? this.plugin.statsManager.getDailySentences()
: 0);
break;
case MetricType.total:
display =
display +
(await (this.plugin.settings.collectStats
? this.plugin.statsManager.getTotalSentences()
: 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:
display = display + 0;
break;
case MetricType.daily:
display =
display +
(this.plugin.settings.collectStats
? this.plugin.statsManager.getDailyPages()
: 0);
break;
case MetricType.total:
display =
display +
(await (this.plugin.settings.collectStats
? this.plugin.statsManager.getTotalPages()
: 0));
break;
}
} else if (metric.counter === MetricCounter.files) {
switch (metric.type) {
case MetricType.file:
display =
display +
(this.plugin.settings.collectStats
? this.plugin.statsManager.getTotalFiles()
: 0);
break;
case MetricType.daily:
display =
display +
(this.plugin.settings.collectStats
? this.plugin.statsManager.getTotalFiles()
: 0);
break;
case MetricType.total:
display =
display +
(this.plugin.settings.collectStats
? this.plugin.statsManager.getTotalFiles()
: 0);
break;
}
} }
display = display + sbItem.suffix; display = display + sbItem.suffix;

View file

@ -3,7 +3,7 @@ import { MATCH_HTML_COMMENT, MATCH_COMMENT } from "src/constants";
export function getWordCount(text: string): number { export function getWordCount(text: string): number {
const spaceDelimitedChars = const spaceDelimitedChars =
/'A-Za-z\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC/ /A-Za-z\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AD\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC/
.source; .source;
const nonSpaceDelimitedWords = const nonSpaceDelimitedWords =
/\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u4E00-\u9FD5/.source; /\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u4E00-\u9FD5/.source;
@ -27,23 +27,6 @@ export function getCharacterCount(text: string): number {
return text.length; 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 { export function getSentenceCount(text: string): number {
const sentences: number = ( const sentences: number = (
(text || "").match( (text || "").match(
@ -54,10 +37,6 @@ export function getSentenceCount(text: string): number {
return sentences; return sentences;
} }
export function getPageCount(text: string, pageWords: number): number {
return parseFloat((getWordCount(text) / pageWords).toFixed(1));
}
export function getTotalFileCount(vault: Vault): number { export function getTotalFileCount(vault: Vault): number {
return vault.getMarkdownFiles().length; return vault.getMarkdownFiles().length;
} }