monaco editor(VS CODEの中のエディタ)のIME不具合を直す
先日ベータ版をリリースしたマークダウンエディタ simple MD
一応ベータ版ができて友人に使ってもらっていたんだけど
バグを発見したということで詳しく聞いてみると・・・
monaco editorの不具合っぽい。
試しに今使っているVS CODEでも再現できるか試したところがっつり再現。
確認したーバージョンは以下の通りです。
VS CODE: 1.107.1
monaco editor: 0.55.1
内容はこうです。
IMEで日本語入力時変換候補がない状態かつ未確定の場合にカーソルキーを押すと、入力項目がずれてしまい、本来の位置とは別に移動した位置にも上書きで文字が入力されてしまう不具合です。
ということでライブラリ側で直すのが正しいとは思いますが、いつ修正がはいるかわからないので
アプリ側で直せるかどうか色々調べてみました。
monaco editorにIMEでの入力の開始、終了のイベントはある(onDidCompositionStart,onDidCompositionEnd)
IME入力中(未確定の文字)は取得できない(=文字列の長さも取得できない)
IME入力中(変換候補でてきる間)はキーイベントも取れない(monaco editor側の)
カーソル位置はとれるっぽいがIME変換中かどうか判断できない
カーソル移動のイベントは取れるがどのキーで移動したかまではわからない
document側のIMEイベントもうまく取れない
はい。はっきりいってかなり詰んでます。
が、行の文字列は変換中の文字列も含んで取れるのでここからどうにかできないか考えたのが以下の方法です。
IME入力前のカーソル位置を取得しておく
IMEの入力が始まったらその行の文字数の長さを取得
IME入力中の行の文字列の長さを取得して2との差で文字列の長さを取得
3の長さからIMEの入力中の終点のカーソル位置を割り出す
IMEカーソルの始点・終点のカーソル範囲から移動しようとしたら元の位置にカーソルを戻す
以上で(一応ですが)対応できました。
simple MDのソースの一部を抜粋
//typescript
editor = monaco.editor.create(this.#containerElement, this.#options);
this.#session = monaco.editor.createModel(this.#textContent ,this.#language);
editor.setModel(this.#session);
//IME入力開始のイベント
editor.onDidCompositionStart(() => {
//IMEの入力中かのフラグ
isComposing = true;
//文字列の長さの取得
this.#isCommingStartLineLength =
this.#session.getLineMaxColumn(editor.getPosition()?.lineNumber || 1);
//IME入力開始のカーソル位置取得
preeditStartPos= this.#beforePosition?.clone() || null;
//前回のカーソル位置保存
this.#isComminBeforePosition = this.#beforePosition?.clone() || null;
});
// カーソル位置が変更されたときのイベント
editor.onDidChangeCursorPosition((event) => {
preeditStartPos = preeditStartPos as monaco.Position;
let isRangeOutside = false;
if(isComposing){
//1行の文字列を取得
const lineMaxColumn = this.#session.getLineMaxColumn(event.position.lineNumber);
const rageCheckPos = {
lineNumber: event.position.lineNumber,
column: preeditStartPos.column + (lineMaxColumn - this.#isCommingStartLineLength!)
};
//範囲外かどうかの判定
if(preeditStartPos.lineNumber !== rageCheckPos.lineNumber){
isRangeOutside = true;
}else if(preeditStartPos.column > event.position.column || event.position.column > rageCheckPos?.column){
isRangeOutside = true;
}
if(isRangeOutside){
//範囲外にでたら1つ前のカーソル位置に戻す
editor.setPosition({
lineNumber: preeditStartPos.lineNumber,
column: this.#isComminBeforePosition!.column
});
}
}
//前回のカーソル位置(IME入力中の)を取得する
this.#beforePosition = event.position.clone();
if(isComposing){
if(!isRangeOutside) this.#isComminBeforePosition = event.position.clone();
}
});
とうかこのあたりのバグはIME使う言語の人たちしか気づかないよな~


コメント