Stackを使ってscrapVimを作れないか
試したこと
何がだめだったか
ddとdなど、一部が重複するキーの区別ができない
escキーが効かない
考えたこと
keyboardのemulation
これは別機能として設計・テストする
classの設計
keyをためるとこ
キーを監視するとこ
キーからコマンドを解釈するとこ
コマンドを実行するとこ
実際のカーソル操作をやるとこ
その他
レジスタ
key mapの読み込み
どうコマンドを実行するか
コマンドの実行は別phaseだ
classを分けよう
完全一致する候補がひとつだけのとき
直ちにコマンドを確定・実行する
複数の候補があるとき
しばらく経ったら最初にマッチした候補で確定する
例:jとjjという2つのコマンドを定義していたとき
operatorの範囲入力待ち
入力されるまで待つ
commandの並びが無効、つまりどれとも一致しなかったら終了する
stackを空にする
予めコマンド候補を作っておく必要がある
code:mock2.js
const commandList =[
]
↑これはだめだ
operatorやmotionと、色んな種類のcommandがある
commandごとに分けたほうが良い
構文解析っぽくやる?
e.g dの後はmotionかdかtext objectのどれかがくる。それ以外の場合は終了
設定ファイルでカスタマイズしたmappingも反映させる
これは一旦後回しにしよう
stackはリスト形式にする
Vim記法のkey codeでどんどんためていく
コマンド実行後にクリアする
code:mock1.js
import {isMobile} from '/api/code/takker/mobile版scrapboxの判定/script.js';
export class KeyStack {
constructor() {
if (isMobile()) {
this._enabled = false;
return;
}
this._enabled = true;
this._stack = [];
this._editor = document.getElementById('editor');
this.onstackupdate = undefined;
this.onflush = undefined;
}
// keyの監視を開始。
start() {
if (!this._enabled) return;
this._editor.addEventListener('keydown', e =>{
const keymap = convertKeyCode(e.key, e);
if (keymap === '') return;
this.push(keymap);
});
this._editor.addEventListener('stackupdate', e => this.onstackupdate(e));
this._editor.addEventListener('stackflush', e => this.onflush(e));
}
stop(){}
// keyをstackする
// 配列を使って複数のkeysを一度に入れられる
push(...keys) {
this._stack.push(keys);
// キーが追加されたというeventを発火する
this._editor.dispatchEvent(
new CustomEvent('stackupdate', {
bubbles: true,
}
// stackの中身を出しつつ、this_stackを空っぽにする
flush() {
this._editor.dispatchEvent(
new CustomEvent('stackflush', {
bubbles: true,
this._stack = [];
}
}
printable key以外は無視
code:mock1.js
export function convertKeyCode(key, {ctrlKey,shiftKey,altKey}) {
// 文字入力の場合
if (key.length === 1 && key !== ' ') {
// どれか一つのmeta keyしか有効にしない
if (altKey) return <A-${key}>;
if (ctrlKey) return <C-${key}>;
return key;
// Shift keyの情報は文字に反映されているので何もしない
}
// 特殊なキー
const specialKeys = {
Backspace: 'BS',
Tab: 'Tab',
Enter: 'CR',
Delete: 'Del',
Escape: 'Esc',
' ': 'Space',
PageUp: 'PageUp',
PageDown: 'PageDown',
End: 'End',
Home: 'Home',
ArrowLeft: 'Left',
ArrowUp: 'Up',
ArrowRight: 'Right',
ArrowDown: 'Down',
F1: 'F1',
F2: 'F2',
F3: 'F3',
F4: 'F4',
F5: 'F5',
F6: 'F6',
F7: 'F7',
F8: 'F8',
F9: 'F9',
F10: 'F10',
F11: 'F11',
F12: 'F12',
};
// どれか一つのmeta keyしか有効にしない
if (altKey) return <A-${specialKeys[key]}>;
if (ctrlKey) return <C-${specialKeys[key]}>;
if (shiftKey) return <S-${specialKeys[key]}>;
return <${specialKeys[key]}>;
}
return '';
}
見た目
code:mock1.css
}
mock1.jsのテストコード
code:mock1-test.js
import {KeyStack} from '/api/code/takker/Stackを使ってscrapVimを作れないか/mock1.js';
const app = document.getElementsByClassName('app')0; app.insertAdjacentHTML('beforeend', `
<style>
@import '/api/code/takker/Stackを使ってscrapVimを作れないか/mock1.css';
</style>
`);
const scrapVimStatusBar = document.createElement('div');
scrapVimStatusBar.id = 'scrapvim-status-bar';
scrapVimStatusBar.classList.add('status-bar');
app.appendChild(scrapVimStatusBar);
const keyStack = new KeyStack();
keyStack.onstackupdate = e => {
// Escapeを押したらflushする
if (e.detail.newKeys.includes('<Esc>')) {
keyStack.flush();
return;
}
console.log(e);
scrapVimStatusBar.textContent = e.detail.stack.join('');
};
keyStack.onflush = e => {
console.log(e);
scrapVimStatusBar.textContent = '';
};
keyStack.start();
11:33:34 stackはこれでよさそう
実行方法
1文字移動→矢印キー
単語移動→ctrl+←→
ただこれをやると、visual modeで<>をつかったインデントの上げ下げが難しくなる
W/E/f/text object
特定の文字を探す
探した文字がcursorから何文字前/後ろにあるかを計算する
削除
DeleteとBackspace連打
切り取り
Shift+矢印キーで範囲選択してからDelete
貼り付け
レジスタの値を流し込む
ctrl+vを実行できないので、clipboardから貼り付けるのは困難かも
リンクをクリック
DOMから検索
cursorの位置と座標を比較する
一番近い文字を探す
範囲選択
ふつうの & 行選択
Shift+矢印キー
<>用に、選択されている行を記憶しておく必要がある
実装するコマンド
d
y
c
p
~
<>
hjkl
gg
G
^0$
wWbBeE
ge
gE
(){}
%HML
f{char}
.
a
i
s
scroll
<c-d><c-f>
<c-u><c-b>
register
"a, "b, ...