getting closer

This commit is contained in:
Luke Leppan 2021-07-07 14:34:39 +02:00
parent 49ad4d12f5
commit 7c420b0b0b
11 changed files with 320 additions and 257 deletions

3
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"window.title": "${dirty} ${rootName} 🦄"
}

View file

@ -17,11 +17,11 @@ All Optional:
Works with all languages. Works with all languages.
#### Coming soon: #### TODO:
- Complete Customization - [ ] add total counts back
- Customization presets - [ ] more statistic display
- More stats - [ ] add more statistics (make suggestions)
### Contributors ### Contributors

View file

@ -25,6 +25,7 @@
"@tsconfig/svelte": "^1.0.13", "@tsconfig/svelte": "^1.0.13",
"@types/moment": "^2.13.0", "@types/moment": "^2.13.0",
"@types/node": "^14.17.3", "@types/node": "^14.17.3",
"@types/parsimmon": "^1.10.6",
"obsidian": "https://github.com/obsidianmd/obsidian-api/tarball/master", "obsidian": "https://github.com/obsidianmd/obsidian-api/tarball/master",
"rollup": "^2.32.1", "rollup": "^2.32.1",
"rollup-plugin-copy": "^3.3.0", "rollup-plugin-copy": "^3.3.0",
@ -36,6 +37,7 @@
"typescript": "^4.0.3" "typescript": "^4.0.3"
}, },
"dependencies": { "dependencies": {
"parsimmon": "^1.18.0",
"svelte": "^3.38.3" "svelte": "^3.38.3"
} }
} }

View file

@ -1 +1,5 @@
%word_count% words %character_count% characters Query:
{word_count} words {character_count} characters
Alt Query:
{files} files {total_words} words {total_characters} characters

View file

@ -8,25 +8,33 @@ export class DataCollector {
this.vault = vault; this.vault = vault;
} }
async getTotalCount(): Promise<any> { getTotalWordCount() {
let allWords: number; let allWords: number = 0;
console.log(allWords);
}
async getTotalCharacterCount() {
let allCharacters: number; let allCharacters: number;
let allSentences: number;
let allFiles: number;
for (const f of this.vault.getFiles()) { for (const f of this.vault.getFiles()) {
let fileContents = await this.vault.cachedRead(f); let fileContents = await this.vault.cachedRead(f);
allWords += this.getWordCount(fileContents);
allCharacters += this.getCharacterCount(fileContents); allCharacters += this.getCharacterCount(fileContents);
}
return allCharacters;
}
async getTotalSentenceCount() {
let allSentences: number;
for (const f of this.vault.getFiles()) {
let fileContents = await this.vault.cachedRead(f);
allSentences += this.getSentenceCount(fileContents); allSentences += this.getSentenceCount(fileContents);
} }
allFiles = this.vault.getFiles().length;
return { return allSentences;
words: allWords, }
characters: allCharacters,
sentences: allSentences, async getTotalFileCount() {
files: allFiles, return this.vault.getFiles().length;
};
} }
getWordCount(text: string): number { getWordCount(text: string): number {

View file

@ -23,7 +23,6 @@ export class DataManager {
} }
this.stats = JSON.parse(await this.vault.adapter.read(".vault-stats")); this.stats = JSON.parse(await this.vault.adapter.read(".vault-stats"));
console.log(this.stats);
this.getTodayIndex(); this.getTodayIndex();
this.update(); this.update();
@ -74,14 +73,13 @@ export class DataManager {
} }
onVaultModify(file: TAbstractFile) { onVaultModify(file: TAbstractFile) {
console.log(this.stats);
if (!this.stats.history[this.index].modifiedFiles.includes(file.name)) { if (!this.stats.history[this.index].modifiedFiles.includes(file.name)) {
console.log(this.stats);
this.stats.history[this.index].modifiedFiles.push(file.name); this.stats.history[this.index].modifiedFiles.push(file.name);
this.update(); this.update();
} }
} }
change(cm: CodeMirror.Editor) {}
setTotalStats() {} setTotalStats() {}
} }

View file

@ -1,6 +1,6 @@
import { MarkdownView, Plugin, TFile, addIcon, WorkspaceLeaf } from "obsidian"; import { MarkdownView, Plugin, TFile, addIcon, WorkspaceLeaf } from "obsidian";
import { BetterWordCountSettingsTab } from "./settings/settings-tab"; import { BetterWordCountSettingsTab } from "./settings/settings-tab";
import { BetterWordCountSettings } from "./settings/settings"; import { BetterWordCountSettings, DEFAULT_SETTINGS } from "./settings/settings";
import { import {
getWordCount, getWordCount,
getCharacterCount, getCharacterCount,
@ -11,14 +11,17 @@ import { StatusBar } from "./status/bar";
import { STATS_ICON, STATS_ICON_NAME, VIEW_TYPE_STATS } from "./constants"; import { STATS_ICON, STATS_ICON_NAME, VIEW_TYPE_STATS } from "./constants";
import StatsView from "./view/view"; import StatsView from "./view/view";
import { DataManager } from "./data/manager"; import { DataManager } from "./data/manager";
import { BarManager } from "./status/manager";
import type CodeMirror from "codemirror";
import { parse } from "./status/parse";
export default class BetterWordCount extends Plugin { export default class BetterWordCount extends Plugin {
public recentlyTyped: boolean;
public statusBar: StatusBar; public statusBar: StatusBar;
public currentFile: TFile; public currentFile: TFile;
public settings: BetterWordCountSettings; public settings: BetterWordCountSettings;
public view: StatsView; public view: StatsView;
public dataManager: DataManager; public dataManager: DataManager;
public barManager: BarManager;
onunload(): void { onunload(): void {
this.app.workspace this.app.workspace
@ -27,26 +30,22 @@ export default class BetterWordCount extends Plugin {
} }
async onload() { async onload() {
this.dataManager = new DataManager(this.app.vault); this.settings = Object.assign(DEFAULT_SETTINGS, await this.loadData());
this.saveData(this.settings);
this.addSettingTab(new BetterWordCountSettingsTab(this.app, this));
let statusBarEl = this.addStatusBarItem(); let statusBarEl = this.addStatusBarItem();
this.statusBar = new StatusBar(statusBarEl); this.statusBar = new StatusBar(statusBarEl);
this.recentlyTyped = false; this.dataManager = new DataManager(this.app.vault);
this.barManager = new BarManager(
this.settings = (await this.loadData()) || new BetterWordCountSettings(); this.statusBar,
this.addSettingTab(new BetterWordCountSettingsTab(this.app, this)); this.settings,
this.app.vault
addIcon(STATS_ICON_NAME, STATS_ICON);
this.updateAltCount();
this.registerEvent(
this.app.workspace.on("file-open", this.onFileOpen, this)
); );
this.registerEvent( this.registerEvent(
this.app.workspace.on("quick-preview", this.onQuickPreview, this) this.app.workspace.on("active-leaf-change", this.activeLeafChange, this)
); );
this.registerEvent( this.registerEvent(
@ -57,9 +56,17 @@ export default class BetterWordCount extends Plugin {
) )
); );
this.registerCodeMirror((cm: CodeMirror.Editor) => {
cm.on("cursorActivity", (cm: CodeMirror.Editor) =>
this.barManager.cursorActivity(cm)
);
});
addIcon(STATS_ICON_NAME, STATS_ICON);
this.addCommand({ this.addCommand({
id: "show-vault-stats-view", id: "show-vault-stats-view",
name: "Open view", name: "Open Statistics",
checkCallback: (checking: boolean) => { checkCallback: (checking: boolean) => {
if (checking) { if (checking) {
return this.app.workspace.getLeavesOfType("vault-stats").length === 0; return this.app.workspace.getLeavesOfType("vault-stats").length === 0;
@ -73,132 +80,19 @@ export default class BetterWordCount extends Plugin {
(leaf: WorkspaceLeaf) => (this.view = new StatsView(leaf)) (leaf: WorkspaceLeaf) => (this.view = new StatsView(leaf))
); );
this.registerInterval(
window.setInterval(async () => {
let activeLeaf = this.app.workspace.activeLeaf;
if (!activeLeaf || !(activeLeaf.view instanceof MarkdownView)) {
return;
}
let editor = activeLeaf.view.sourceMode.cmEditor;
if (editor.somethingSelected()) {
let content: string = editor.getSelection();
this.updateWordCount(content);
this.recentlyTyped = false;
} else if (
this.currentFile &&
this.currentFile.extension === "md" &&
!this.recentlyTyped
) {
const contents = await this.app.vault.cachedRead(this.currentFile);
this.updateWordCount(contents);
} else if (!this.recentlyTyped) {
this.updateWordCount("");
}
}, 500)
);
let activeLeaf = this.app.workspace.activeLeaf;
let files: TFile[] = this.app.vault.getMarkdownFiles();
files.forEach((file) => {
if (file.basename === activeLeaf.getDisplayText()) {
this.onFileOpen(file);
}
});
if (this.app.workspace.layoutReady) { if (this.app.workspace.layoutReady) {
this.initLeaf(); this.initLeaf();
} }
} }
async onFileOpen(file: TFile) { activeLeafChange(leaf: WorkspaceLeaf) {
this.currentFile = file; if (!(leaf.view.getViewType() === "markdown")) {
if (file && file.extension === "md") { this.barManager.updateAltStatusBar();
const contents = await this.app.vault.cachedRead(file);
this.recentlyTyped = true;
this.updateWordCount(contents);
} else {
this.updateAltCount();
} }
} }
onQuickPreview(file: TFile, contents: string) { async saveSettings(): Promise<void> {
this.currentFile = file; await this.saveData(this.settings);
const leaf = this.app.workspace.activeLeaf;
if (leaf && leaf.view.getViewType() === "markdown") {
this.recentlyTyped = true;
this.updateWordCount(contents);
}
}
async updateAltCount() {
const files = getFilesCount(this.app.vault.getFiles());
let displayText: string = `${files} files `;
let allWords = 0;
let allCharacters = 0;
let allSentences = 0;
for (const f of this.app.vault.getMarkdownFiles()) {
let fileContents = await this.app.vault.cachedRead(f);
allWords += getWordCount(fileContents);
allCharacters += getCharacterCount(fileContents);
allSentences += getSentenceCount(fileContents);
}
if (this.settings.showWords) {
displayText =
displayText +
this.settings.wordsPrefix +
allWords +
this.settings.wordsSuffix;
}
if (this.settings.showCharacters) {
displayText =
displayText +
this.settings.charactersPrefix +
allCharacters +
this.settings.charactersSuffix;
}
if (this.settings.showSentences) {
displayText =
displayText +
this.settings.sentencesPrefix +
allSentences +
this.settings.sentencesSuffix;
}
this.statusBar.displayText(displayText);
}
updateWordCount(text: string) {
let displayText: string = "";
if (this.settings.showWords) {
displayText =
displayText +
this.settings.wordsPrefix +
getWordCount(text) +
this.settings.wordsSuffix;
}
if (this.settings.showCharacters) {
displayText =
displayText +
this.settings.charactersPrefix +
getCharacterCount(text) +
this.settings.charactersSuffix;
}
if (this.settings.showSentences) {
displayText =
displayText +
this.settings.sentencesPrefix +
getSentenceCount(text) +
this.settings.sentencesSuffix;
}
this.statusBar.displayText(displayText);
} }
initLeaf(): void { initLeaf(): void {

View file

@ -1,106 +1,78 @@
import { App, PluginSettingTab, Setting } from "obsidian"; import {
App,
DropdownComponent,
PluginSettingTab,
Setting,
TextAreaComponent,
ToggleComponent,
} from "obsidian";
import type BetterWordCount from "src/main"; import type BetterWordCount from "src/main";
import { PRESETS, PresetOption } from "../settings/settings";
export class BetterWordCountSettingsTab extends PluginSettingTab { export class BetterWordCountSettingsTab extends PluginSettingTab {
constructor(app: App, plugin: BetterWordCount) { private disableTextAreas: boolean;
constructor(app: App, private plugin: BetterWordCount) {
super(app, plugin); super(app, plugin);
this.disableTextAreas =
this.plugin.settings.preset.name === "custom" ? false : true;
} }
display(): void { display(): void {
let { containerEl } = this; let { containerEl } = this;
const plugin: BetterWordCount = (this as any).plugin;
containerEl.empty(); containerEl.empty();
containerEl.createEl("h2", { text: "Better Word Count Settings" }); containerEl.createEl("h2", { text: "Better Word Count Settings" });
// Word Count Settings // General Settings
containerEl.createEl("h3", { text: "Word Count Settings" }); containerEl.createEl("h3", { text: "General Settings" });
new Setting(containerEl) new Setting(containerEl)
.setName("Show Word Count") .setName("Collect Statistics")
.setDesc("Enable this to show the word count.") .setDesc(
.addToggle((boolean) => "Turn on to start collecting daily statistics of your writing. Stored in the .vault-stats file in the root of your vault."
boolean.setValue(plugin.settings.showWords).onChange((value) => { )
plugin.settings.showWords = value; .addToggle((cb: ToggleComponent) => {
plugin.saveData(plugin.settings); cb.setValue(this.plugin.settings.collectStats);
}) cb.onChange(async (value: boolean) => {
); this.plugin.settings.collectStats = value;
new Setting(containerEl) await this.plugin.saveSettings();
.setName("Word Count Prefix") });
.setDesc("This changes the text in front of the word count number.") });
.addText((text) =>
text.setValue(plugin.settings.wordsPrefix).onChange((value) => {
plugin.settings.wordsPrefix = value;
plugin.saveData(plugin.settings);
})
);
new Setting(containerEl)
.setName("Word Count Suffix")
.setDesc("This changes the text after of the word count number.")
.addText((text) =>
text.setValue(plugin.settings.wordsSuffix).onChange((value) => {
plugin.settings.wordsSuffix = value;
plugin.saveData(plugin.settings);
})
);
// Character Count Settings // Status Bar Settings
containerEl.createEl("h3", { text: "Character Count Settings" }); containerEl.createEl("h3", { text: "Status Bar Settings" });
new Setting(containerEl) new Setting(containerEl)
.setName("Show Character Count") .setName("Select a Preset")
.setDesc("Enable this to show the character count.") .setDesc(
.addToggle((boolean) => "Presets are premade status bar expressions. Overides status bar settings."
boolean.setValue(plugin.settings.showCharacters).onChange((value) => { )
plugin.settings.showCharacters = value; .addDropdown((cb: DropdownComponent) => {
plugin.saveData(plugin.settings); PRESETS.forEach((preset: PresetOption) => {
}) cb.addOption(preset.name, preset.name);
); });
new Setting(containerEl) cb.setValue(this.plugin.settings.preset.name);
.setName("Character Count Prefix")
.setDesc("This changes the text in front of the character count number.")
.addText((text) =>
text.setValue(plugin.settings.charactersPrefix).onChange((value) => {
plugin.settings.charactersPrefix = value;
plugin.saveData(plugin.settings);
})
);
new Setting(containerEl)
.setName("Character Count Suffix")
.setDesc("This changes the text after of the character count number.")
.addText((text) =>
text.setValue(plugin.settings.charactersSuffix).onChange((value) => {
plugin.settings.charactersSuffix = value;
plugin.saveData(plugin.settings);
})
);
// Sentence Count Settings cb.onChange(async (value: string) => {
containerEl.createEl("h3", { text: "Sentence Count Settings" }); let newPreset = PRESETS.find((preset) => preset.name === value);
this.plugin.settings.preset = newPreset;
this.plugin.settings.statusBarQuery = newPreset.statusBarQuery;
this.plugin.settings.statusBarAltQuery = newPreset.statusBarAltQuery;
await this.plugin.saveSettings();
});
});
new Setting(containerEl) new Setting(containerEl)
.setName("Show Sentence Count") .setName("Status Bar Text")
.setDesc("Enable this to show the sentence count.") .setDesc("Customize the Status Bar text with this.")
.addToggle((boolean) => .addTextArea((cb: TextAreaComponent) => {
boolean.setValue(plugin.settings.showSentences).onChange((value) => { cb.setPlaceholder("Enter an expression...");
plugin.settings.showSentences = value; cb.setValue(this.plugin.settings.statusBarQuery);
plugin.saveData(plugin.settings); });
})
);
new Setting(containerEl) new Setting(containerEl)
.setName("Sentence Count Prefix") .setName("Alternative Status Bar Text")
.setDesc("This changes the text in front of the sentence count number.") .setDesc("Customize the Alternative Status Bar text with this.")
.addText((text) => .addTextArea((cb: TextAreaComponent) => {
text.setValue(plugin.settings.sentencesPrefix).onChange((value) => { cb.setPlaceholder("Enter an expression...");
plugin.settings.sentencesPrefix = value; cb.setValue(this.plugin.settings.statusBarAltQuery);
plugin.saveData(plugin.settings); });
})
);
new Setting(containerEl)
.setName("Sentence Count Suffix")
.setDesc("This changes the text after of the sentence count number.")
.addText((text) =>
text.setValue(plugin.settings.sentencesSuffix).onChange((value) => {
plugin.settings.sentencesSuffix = value;
plugin.saveData(plugin.settings);
})
);
} }
} }

View file

@ -1,11 +1,46 @@
export class BetterWordCountSettings { export const DEFAULT_SETTINGS: BetterWordCountSettings = {
showWords: boolean = true; preset: {
wordsPrefix: string = ""; name: "default",
wordsSuffix: string = " words "; statusBarQuery: "{word_count} words {character_count} characters",
showCharacters: boolean = true; statusBarAltQuery:
charactersPrefix: string = ""; "{files} files {total_words} words {total_characters} characters",
charactersSuffix: string = " characters "; },
showSentences: boolean = false; statusBarQuery: "{word_count} words {character_count} characters",
sentencesPrefix: string = ""; statusBarAltQuery:
sentencesSuffix: string = " sentences"; "{files} files {total_words} words {total_characters} characters",
countComments: false,
collectStats: false,
};
export const PRESETS: PresetOption[] = [
{
name: "default",
statusBarQuery: "{word_count} words {character_count} characters",
statusBarAltQuery:
"{files} files {total_words} words {total_characters} characters",
},
{
name: "minimal",
statusBarQuery: "w: {word_count} c: {character_count}",
statusBarAltQuery: "f: {files} tw: {total_words} tc: {total_characters}",
},
{
name: "custom",
statusBarQuery: "",
statusBarAltQuery: "",
},
];
export interface BetterWordCountSettings {
preset: PresetOption;
statusBarQuery: string;
statusBarAltQuery: string;
countComments: boolean;
collectStats: boolean;
}
export interface PresetOption {
name: string;
statusBarQuery: string;
statusBarAltQuery: string;
} }

View file

@ -1,7 +1,112 @@
import type { Vault } from "obsidian";
import { DataCollector } from "src/data/collector";
import type { BetterWordCountSettings } from "src/settings/settings";
import {
getWordCount,
getCharacterCount,
getSentenceCount,
} from "../data/stats";
import type { StatusBar } from "./bar"; import type { StatusBar } from "./bar";
import { Expression, parse } from "./parse";
export class BarManager { export class BarManager {
private statusBar: StatusBar; private statusBar: StatusBar;
private settings: BetterWordCountSettings;
private dataCollector: DataCollector;
constructor(statusBar: StatusBar) {} constructor(
statusBar: StatusBar,
settings: BetterWordCountSettings,
vault: Vault
) {
this.statusBar = statusBar;
this.settings = settings;
this.dataCollector = new DataCollector(vault);
}
async updateStatusBar(text: string): Promise<void> {
let newText = "";
const expression: Expression = parse(this.settings.statusBarQuery);
let varsIndex = 0;
expression.parsed.forEach((value: string) => {
newText = newText + value;
switch (expression.vars[varsIndex]) {
case 0:
newText = newText + getWordCount(text);
break;
case 1:
newText = newText + getCharacterCount(text);
break;
case 2:
newText = newText + getSentenceCount(text);
break;
case 3:
newText = newText + this.dataCollector.getTotalWordCount();
break;
case 4:
newText = newText + this.dataCollector.getTotalCharacterCount();
break;
case 5:
newText = newText + this.dataCollector.getTotalSentenceCount();
break;
case 6:
newText = newText + this.dataCollector.getTotalFileCount();
break;
}
varsIndex++;
});
this.statusBar.displayText(newText);
}
async updateAltStatusBar(): Promise<void> {
let newText = "";
const expression: Expression = parse(this.settings.statusBarAltQuery);
let varsIndex = 0;
expression.parsed.forEach(async (value: string) => {
newText = newText + value;
switch (expression.vars[varsIndex]) {
case 0:
newText = newText + getWordCount("");
break;
case 1:
newText = newText + getCharacterCount("");
break;
case 2:
newText = newText + getSentenceCount("");
break;
case 3:
newText = newText + (await this.dataCollector.getTotalWordCount());
break;
case 4:
newText =
newText + (await this.dataCollector.getTotalCharacterCount());
break;
case 5:
newText =
newText + (await this.dataCollector.getTotalSentenceCount());
break;
case 6:
newText = newText + (await this.dataCollector.getTotalFileCount());
break;
}
varsIndex++;
});
this.statusBar.displayText(newText);
}
cursorActivity(cm: CodeMirror.Editor) {
if (cm.somethingSelected()) {
this.updateStatusBar(cm.getSelection());
} else {
this.updateStatusBar(cm.getValue());
}
}
// change(cm: CodeMirror.Editor, changeObj: CodeMirror.EditorChangeLinkedList) {
// this.updateStatusBar(cm.getValue());
// }
} }

View file

@ -1,14 +1,56 @@
const REGEX: RegExp = /{(.*?)}/g;
export interface Expression { export interface Expression {
parsedText: string; parsed: string[];
vars: number[]; vars: number[];
} }
export function parse(text: string): Expression { // Could be done smarter but I'm tired
let parsedText: string; export function parse(query: string): Expression {
let vars: number[]; let parsed: string[] = [];
let vars: number[] = [];
query.split(REGEX).forEach((s) => {
switch (s) {
case "word_count":
vars.push(0);
break;
case "character_count":
vars.push(1);
break;
case "sentence_count":
vars.push(2);
break;
case "total_word_count":
vars.push(3);
break;
case "total_character_count":
vars.push(4);
break;
case "total_sentence_count":
vars.push(5);
break;
case "file_count":
vars.push(6);
break;
default:
parsed.push(s);
break;
}
});
return { return {
parsedText: parsedText, parsed: parsed,
vars: vars, vars: vars,
}; };
} }
const varNames = {
word_count: 0,
charater_count: 1,
sentence_count: 2,
total_word_count: 3,
total_charater_count: 4,
total_sentence_count: 5,
file_count: 6,
};