first commit
This commit is contained in:
commit
0f1cc5ecd8
9 changed files with 207 additions and 0 deletions
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Intellij
|
||||||
|
*.iml
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# npm
|
||||||
|
node_modules
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
|
# build
|
||||||
|
main.js
|
||||||
|
*.js.map
|
||||||
|
test-vault/
|
||||||
|
*.zip
|
7
README.md
Normal file
7
README.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
## Better Word Count
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
**Note:** This plugin has only been tested with English.
|
||||||
|
|
||||||
|

|
BIN
assets/better-word-count.gif
Normal file
BIN
assets/better-word-count.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 600 KiB |
9
manifest.json
Normal file
9
manifest.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"id": "better-word-count",
|
||||||
|
"name": "Better Word Count",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Counts the words of selected test in the editor.",
|
||||||
|
"author": "Luke Leppan",
|
||||||
|
"authorUrl": "https://lukeleppan.com",
|
||||||
|
"isDesktopOnly": false
|
||||||
|
}
|
28
package.json
Normal file
28
package.json
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"name": "better-word-count",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Counts the words of selected test in the editor.",
|
||||||
|
"main": "main.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "rollup --config rollup.config.js -w",
|
||||||
|
"build": "rollup --config rollup.config.js"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"obsidian",
|
||||||
|
"obsidian-md",
|
||||||
|
"obsidian-plugin"
|
||||||
|
],
|
||||||
|
"author": "Luke Leppan",
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@rollup/plugin-commonjs": "^15.1.0",
|
||||||
|
"@rollup/plugin-node-resolve": "^9.0.0",
|
||||||
|
"@rollup/plugin-typescript": "^6.0.0",
|
||||||
|
"@types/node": "^14.14.2",
|
||||||
|
"obsidian": "https://github.com/obsidianmd/obsidian-api/tarball/master",
|
||||||
|
"rollup": "^2.32.1",
|
||||||
|
"rollup-plugin-copy": "^3.3.0",
|
||||||
|
"tslib": "^2.0.3",
|
||||||
|
"typescript": "^4.0.3"
|
||||||
|
}
|
||||||
|
}
|
27
rollup.config.js
Normal file
27
rollup.config.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import typescript from '@rollup/plugin-typescript';
|
||||||
|
import {nodeResolve} from '@rollup/plugin-node-resolve';
|
||||||
|
import commonjs from '@rollup/plugin-commonjs';
|
||||||
|
import copy from 'rollup-plugin-copy';
|
||||||
|
const TEST_VAULT = 'test-vault/.obsidian/plugins/better-word-count';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
input: 'src/main.ts',
|
||||||
|
output: {
|
||||||
|
dir: 'dist/',
|
||||||
|
sourcemap: 'inline',
|
||||||
|
format: 'cjs',
|
||||||
|
exports: 'default'
|
||||||
|
},
|
||||||
|
external: ['obsidian'],
|
||||||
|
plugins: [
|
||||||
|
typescript(),
|
||||||
|
nodeResolve({browser: true}),
|
||||||
|
commonjs(),
|
||||||
|
copy({
|
||||||
|
targets: [
|
||||||
|
{ src: 'dist/main.js', dest: TEST_VAULT },
|
||||||
|
{ src: ['manifest.json'], dest: TEST_VAULT }
|
||||||
|
], flatten: true
|
||||||
|
})
|
||||||
|
]
|
||||||
|
};
|
90
src/main.ts
Normal file
90
src/main.ts
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import { App, MarkdownView, Modal, Plugin, TFile } from "obsidian";
|
||||||
|
import { StatusBar } from "./status-bar";
|
||||||
|
|
||||||
|
export default class BetterWordCount extends Plugin {
|
||||||
|
public recentlyTyped: boolean;
|
||||||
|
public statusBar: StatusBar;
|
||||||
|
|
||||||
|
onload() {
|
||||||
|
let statusBarEl = this.addStatusBarItem();
|
||||||
|
this.statusBar = new StatusBar(statusBarEl);
|
||||||
|
|
||||||
|
this.recentlyTyped = false;
|
||||||
|
|
||||||
|
this.registerEvent(
|
||||||
|
this.app.workspace.on("file-open", this.onFileOpen, this)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.registerEvent(
|
||||||
|
this.app.workspace.on("quick-preview", this.onQuickPreview, this)
|
||||||
|
);
|
||||||
|
|
||||||
|
let activeLeaf = this.app.workspace.activeLeaf;
|
||||||
|
let files: TFile[] = this.app.vault.getMarkdownFiles();
|
||||||
|
|
||||||
|
files.forEach((file) => {
|
||||||
|
if ((file.basename = activeLeaf.getDisplayText())) {
|
||||||
|
this.onFileOpen(file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async onFileOpen(file: TFile) {
|
||||||
|
if (file && file.extension === "md") {
|
||||||
|
const contents = await this.app.vault.cachedRead(file);
|
||||||
|
this.updateWordCount(contents);
|
||||||
|
} else {
|
||||||
|
this.updateWordCount("");
|
||||||
|
}
|
||||||
|
|
||||||
|
let activeLeaf = this.app.workspace.activeLeaf;
|
||||||
|
if (!activeLeaf || !(activeLeaf.view instanceof MarkdownView)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let editor = activeLeaf.view.sourceMode.cmEditor;
|
||||||
|
|
||||||
|
activeLeaf.view.registerInterval(
|
||||||
|
window.setInterval(async () => {
|
||||||
|
if (editor.somethingSelected()) {
|
||||||
|
let content: string = editor.getSelection();
|
||||||
|
this.updateWordCount(content);
|
||||||
|
this.recentlyTyped = false;
|
||||||
|
} else if (file && file.extension === "md" && !this.recentlyTyped) {
|
||||||
|
const contents = await this.app.vault.cachedRead(file);
|
||||||
|
this.updateWordCount(contents);
|
||||||
|
} else if (!this.recentlyTyped) {
|
||||||
|
this.updateWordCount("");
|
||||||
|
}
|
||||||
|
}, 500)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
onQuickPreview(file: TFile, contents: string) {
|
||||||
|
const leaf = this.app.workspace.activeLeaf;
|
||||||
|
if (leaf && leaf.view.getViewType() === "markdown") {
|
||||||
|
this.recentlyTyped = true;
|
||||||
|
this.updateWordCount(contents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateWordCount(text: string) {
|
||||||
|
let words = 0;
|
||||||
|
|
||||||
|
const matches = text.match(
|
||||||
|
/[a-zA-Z0-9_\u0392-\u03c9\u00c0-\u00ff\u0600-\u06ff]+|[\u4e00-\u9fff\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\uac00-\ud7af]+/gm
|
||||||
|
);
|
||||||
|
|
||||||
|
if (matches) {
|
||||||
|
for (let i = 0; i < matches.length; i++) {
|
||||||
|
if (matches[i].charCodeAt(0) > 19968) {
|
||||||
|
words += matches[i].length;
|
||||||
|
} else {
|
||||||
|
words += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.statusBar.displayText(`${words} words ` + `${text.length} characters`);
|
||||||
|
}
|
||||||
|
}
|
11
src/status-bar.ts
Normal file
11
src/status-bar.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
export class StatusBar {
|
||||||
|
private statusBarEl: HTMLElement;
|
||||||
|
|
||||||
|
constructor(statusBarEl: HTMLElement) {
|
||||||
|
this.statusBarEl = statusBarEl;
|
||||||
|
}
|
||||||
|
|
||||||
|
displayText(text: string) {
|
||||||
|
this.statusBarEl.setText(text);
|
||||||
|
}
|
||||||
|
}
|
22
tsconfig.json
Normal file
22
tsconfig.json
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"inlineSourceMap": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"target": "es5",
|
||||||
|
"allowJs": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"importHelpers": true,
|
||||||
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"es5",
|
||||||
|
"scripthost",
|
||||||
|
"es2015"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"**/*.ts"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in a new issue