generated at
Stackを使ってscrapVimを作れないか
ScrapVimの実装方法を考えるtakker

試したこと
Mousetrapを使って個別にkeyを実装する
何がだめだったか
dd d など、一部が重複するキーの区別ができない
esc キーが効かない

考えたこと
keyboardのemulation
これは別機能として設計・テストする
classの設計
単一責任の原則を意識する
keyをためるとこ
キーを監視するとこ
キーからコマンドを解釈するとこ
コマンドを実行するとこ
実際のカーソル操作をやるとこ
その他
レジスタ
key mapの読み込み
stackを使ってkeyを認識する
/yutaro/emoji selectorのように、 keydown で捕捉したキーをどんどんためていく
/customize/Scrapbox Dynamic Macroも参考になりそう
どうコマンドを実行するか
コマンドの実行は別phaseだ
classを分けよう
完全一致する候補がひとつだけのとき
直ちにコマンドを確定・実行する
複数の候補があるとき
しばらく経ったら最初にマッチした候補で確定する
setTimeout()を使えば実現できる
例: j jj という2つのコマンドを定義していたとき
operatorの範囲入力待ち
入力されるまで待つ
commandの並びが無効、つまりどれとも一致しなかったら終了する
stackを空にする
この辺、VImの内部構造と一致しているか自信がないな
というかVimの内部構造がどうなっているのかそもそも知らない
予めコマンド候補を作っておく必要がある
mock2.js
const commandList =[ ['h'], ['d','d'] ]
↑これはだめだ
operatorやmotionと、色んな種類のcommandがある
commandごとに分けたほうが良い
構文解析っぽくやる?
e.g d の後はmotionか d かtext objectのどれかがくる。それ以外の場合は終了
設定ファイルでカスタマイズしたmappingも反映させる
これは一旦後回しにしよう
stackはリスト形式にする
Vim記法のkey 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, detail: { stack: [...this._stack], newKeys: [...keys] } })); } // stackの中身を出しつつ、this_stackを空っぽにする flush() { this._editor.dispatchEvent( new CustomEvent('stackflush', { bubbles: true, detail: { stack: [...this._stack] } })); this._stack = []; } }
KeyboardEvent.keyからVimのkey codeに変換する函数
printable key以外は無視
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', }; if (specialKeys[key]) { // どれか一つのmeta keyしか有効にしない if (altKey) return `<A-${specialKeys[key]}>`; if (ctrlKey) return `<C-${specialKeys[key]}>`; if (shiftKey) return `<S-${specialKeys[key]}>`; return `<${specialKeys[key]}>`; } return ''; }
見た目
dark theme UserCSS@0.1.0を使っている
mock1.css
#scrapvim-status-bar.status-bar { border: solid 1px #888; color: #b5b5b5; }
mock1.js のテストコード
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はこれでよさそう
実行方法
scrapbox-keyboard-emulationを使ってhackする
1文字移動→矢印キー
単語移動→ ctrl +
Prioritize Outline-edit key bindingsをOFFにする必要がある
ただこれをやると、visual modeで < > をつかったインデントの上げ下げが難しくなる
単語単位のcursor移動を自作できないかな?
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, ...

#2020-11-17 14:56:15