#VariousComplements ## 経緯 - [Is it possible to add Arabic? · Issue #2 · tadashi-aikawa/obsidian-various-complements-plugin](https://github.com/tadashi-aikawa/obsidian-various-complements-plugin/issues/2) - [Language independent? (Or adding German) · Issue #3 · tadashi-aikawa/obsidian-various-complements-plugin](https://github.com/tadashi-aikawa/obsidian-various-complements-plugin/issues/3) ## やること - [x] ホワイトスペース区切りでトークナイズできるようにする - [x] 日本語とそれ以外でコマンド名を切り替える - [x] 動作確認 - [x] 英語 - [x] 日本語 - [x] ドイツ語 - [x] アラビア語 ### ### ホワイトスペース区切りでトークナイズできるようにする [[CodeMirror]]の`getLineTokens`を使うとそれっぽい結果がとれる。 ```ts function pickTokens(cmEditor: Editor): string[] { const maxLineIndex = cmEditor.getDoc().lineCount(); return [...Array(maxLineIndex).keys()] .flatMap((x) => cmEditor.getLineTokens(x).map((x) => x.string)) .map((x) => x.replace(/[\[\]()<>"'.,|; `]/g, "")) .filter((x) => x !== ""); } ``` ### 日本語とそれ以外でコマンド名を切り替える `tokenizer.ts`を分離して抽象+Factory化。 | コマンド名 | デフォルトショートカット | 説明 | | ------------------------- | ------------------------ | -------------------- | | Auto Complete | `Ctrl + Space` | 空白区切り言語の補完 | | Auto Complete as Japanese | | 日本語の補完 | ```ts import TinySegmenter from "./tiny-segmenter"; import CodeMirror from "codemirror"; // @ts-ignore const segmenter = new TinySegmenter(); export type TokenizeStrategy = "default" | "japanese"; function pickTokens(cmEditor: CodeMirror.Editor): string[] { const maxLineIndex = cmEditor.getDoc().lineCount(); return [...Array(maxLineIndex).keys()] .flatMap((x) => cmEditor.getLineTokens(x).map((x) => x.string)) .map((x) => x.replace(/[\[\]()<>"'.,|; `]/g, "")) .filter((x) => x !== ""); } function pickTokensAsJapanese(cmEditor: CodeMirror.Editor): string[] { return cmEditor .getValue() .split(`\n`) .flatMap<string>((x) => segmenter.segment(x)) .map((x) => x.replace(/[\[\]()<>"'.,|; `]/, "")); } interface TokenizedResult { currentToken: string; currentTokenStart: number; tokens: string[]; } interface Tokenizer { /** * Return undefined if current token is empty. */ tokenize(): TokenizedResult | undefined; } class DefaultTokenizer implements Tokenizer { private readonly cmEditor: CodeMirror.Editor; constructor(cmEditor: CodeMirror.Editor) { this.cmEditor = cmEditor; } tokenize(): TokenizedResult | undefined { const cursor = this.cmEditor.getCursor(); const token = this.cmEditor.getTokenAt(cursor); if (!token.string) { return undefined; } return { currentToken: token.string, currentTokenStart: token.start, tokens: pickTokens(this.cmEditor), }; } } class JapaneseTokenizer implements Tokenizer { private readonly cmEditor: CodeMirror.Editor; constructor(cmEditor: CodeMirror.Editor) { this.cmEditor = cmEditor; } tokenize(): TokenizedResult | undefined { const cursor = this.cmEditor.getCursor(); const token = this.cmEditor.getTokenAt(cursor); if (!token.string) { return undefined; } const words = segmenter.segment(token.string); const currentToken = words.pop(); const currentTokenStart = token.start + words.reduce((t: number, x: string) => t + x.length, 0); const tokens = pickTokensAsJapanese(this.cmEditor); return { currentToken, currentTokenStart, tokens, }; } } export function createTokenizer( cmEditor: CodeMirror.Editor, strategy: TokenizeStrategy ): Tokenizer { switch (strategy) { case "default": return new DefaultTokenizer(cmEditor); case "japanese": return new JapaneseTokenizer(cmEditor); default: throw new Error(`Unexpected strategy name: ${strategy}`); } } ``` ### 動作確認 `Japanese` ``` Obsidian用のこのプラグインを使用すると、マークダウンファイルへの入力を補完できます。 ``` Obsidian用のこのプラグインを使用すると、マークダウンファイルへの入力を補完できます。 `English` ``` This plugin for Obsidian enables you to complement input in markdown files. ``` This plugin for Obsidian enables you to complement input in markdown files. `German` ``` Mit diesem Plugin für Obsidian können Sie Eingaben in Markdown-Dateien ergänzen. ``` ergän `Arabic` ``` يمكّنك هذا المكون الإضافي لـ Obsidian من استكمال الإدخال في ملفات markdown. ``` الإدخال ## 備考 ### code blockの中はスペースが詰まって正しく補完されない問題 typeが`hmd-codeblock`を含む場合は更に空白でtokenize。