目的
長い文章を書いている時、「今書いてる章が何文字になったか」をその場で知りたいことがあります。
でも、カーソルは今書いているところに据え置きにしておきたい。
「Obsidianで日本語小説を書く設定」で紹介した通り、「Better Word Count」プラグインを入れて「Display Section Word Count」をONにすれば章ごとの文字数(※本来ワード数ですが、日本語では実質文字数になる)を表示できるのですが、画面範囲外の見出しをスクロールして見に行くのも、アウトラインから探して移動するのも嫌なのです。
そんなわがまま言われてもな……。
という感じですが、TemplaterプラグインとJavaScriptの合わせ技でこれも解決出来ます。
Windows、Android環境で確認済み。
設定
全文解析というのはそれなりに処理能力を食うものでもありますし、そんなに常時文字数を知りたいわけでもない。
折角BWCという素晴らしいプラグインがあるなら、重複機能を開発するよりは、BWCがカウントしてくれた文字数をワンタッチで見れればいいよね、という思想で、BWCの文字数を拾って表示するスクリプトを書きました。
必要な準備
- Templaterプラグインを入れて、テンプレート用フォルダを指定する
- Better Word Countプラグインを入れる+「Display Section Word Count」をONにする
※これを行っていないと何も出ません。
スクリプト
Templaterプラグインで指定したテンプレート用フォルダに適当なファイル名(例:ShowActiveWordCount.md)でテンプレファイルを作り、以下のスクリプトをコピペして保存してください。
注意:これは以前紹介した「# 1. タイトル」という形式で見出しを構成していることを前提にしたスクリプトです。「.」がいらないとか、スペースを入れたくないとか、そういう違いがある場合はmatchの後の正規表現を適宜いじってください。(まあ、Obsidianでこういうことをしようとする方々へは釈迦に説法かもしれませんが……)
<%*
// Get current section number
const editorAct = this.app.workspace.activeLeaf.view.editor;
const curCur = editorAct.getCursor();
const str = editorAct.getRange(editorAct.offsetToPos(0), curCur);
const lastSec = str.match(/(.*#\s(\d+))\.\s.*?$/s);
let curSecN = 0;
if(lastSec) {
curSecN = Number(lastSec[2]);
// Set cursor (Necessary for when the heading is out of range)
editorAct.setCursor(editorAct.offsetToPos(lastSec[1].length));
}
// Use 'Best Word Count' values
const bwcs = document.getElementsByClassName("bwc-section-count");
for(let bwc of bwcs) {
const sec = bwc.parentElement.innerText.match(/.*#\s(\d+)\.\s.*?$/s);
if(sec) {
if(Number(sec[1]) === curSecN) {
new Notice("Words of current section: " + bwc.innerText);
break;
}
}
}
editorAct.setCursor(curCur);
%>
あとは Obsidianで小説を書くTips:Templaterで章番号を自動挿入 で紹介した通り、タブバーなりお好きな場所にボタンを置けば、ボタン一発で今カーソルがある章の文字数が出るようになります。

↑右上に通知として出て少しすると消えます

↑モバイル版はこんな感じ。
スクリプトの中身についてメモ
以下は機能を使いたいだけの人は見なくていいのですが、スクリプトを書いていて幾つか引っかかったポイントがあったのでメモ的に書いておきます。
HTML化後のデータをTemplaterは取れるのか?
Templaterには親切なドキュメントがあります。
https://silentvoid13.github.io/Templater/introduction.html
が、ここに書かれている tp.* 系の関数はエディタ内のテキスト操作を前提にしています。
Obsidianは描画にはChromeを使っているので、生テキストを表示する時にHTML化します(大雑把な理解)。
Better Word Countsはエディタ内の実際に保存されるテキストには触らず、このHTML化の過程で文字数も仕込んでいる感じなので、エディタ内の生テキストの文字をいくら探しても、カウント済み文字数のデータはありません。
……じゃあ、どうするの?
色々検索するとありがたくも普通にdocumentで取っているサンプルがあったので、一安心しました。つまり、普通のHTMLを扱うのと同じように、お馴染みのgetElementsBy*でもなんでも使えるわけですね。
// ここで取っているのはエディタ側のデータ。
// HTMLは含まないが、実際に保存されるテキストやカーソル位置を取れる。
const editorAct = this.app.workspace.activeLeaf.view.editor;
// ここで取っているのはHTML化された後の描画用データ。
// Better Word Countの文字数データはここにしかない。
const bwcs = document.getElementsByClassName("bwc-section-count");
なので今回は、「まずカーソル位置直上にある見出し番号をエディタから取得し、次にBetter Word Countによる全見出しの文字数リストをHTMLから取得し、見出し番号が合ったものの文字数を表示する」という流れのスクリプトになりました。(もっと効率的なやり方がある気もする……)
HTML構造の調べ方
Obsidian初心者として最初衝撃を受けたのでこれも書いておきますが、内部的にChromeを使っているということで、ObsidianのPC版ではなんとChromeでお馴染みのDeveloper Toolsにアクセス可能です。

Element名を調べたい! とかCSSカスタムしたい! という時はCtrl+Shift+Iで開けます。当然コンソールも使えます。そんなことできていいのか……。
Better Word Countのデータがどの要素に入っているのかも当然これで調べました。便利ですねぇ
ちなみにこのコンソールから「this.app.emulateMobile(true);」を打つとモバイル版のエミュレータにも切り替え可能です。公式にそう書いてある。神か。
https://docs.obsidian.md/Plugins/Getting+started/Mobile+development
描画用データの罠
スクリプトの話に戻って、確認中にたまにうまく文字数が取れないことがあって困りました。
探ったところ、描画範囲外のHTMLデータは適宜捨てられているので、documentに知りたい範囲の見出しデータがそもそも含まれていないことがあるらしい。
ないものは取れない。これは詰んだか? と思いましたが、エディタ側を操作して一瞬カーソルを見出しに移動してみることにしたところ、ちゃんと描画範囲内に入ってデータを読んでくれるようで、うまくいくようになりました。
画面ガタガタするかな? と思いましたが、そうでもなさそうなので、一旦これで実用には足りそうです。
おわりに
プラグインを書かなくてもこれだけのことが出来るObsidian+Templater、本当に無限の可能性を感じますね。
まだ十分理解出来ていない部分も多いのですが、そういう紆余曲折感も含めて引き続き楽しんで使って行こうと思います。
それではよきObsidianライフをʕ ·ᴥ·ʔ
コメント