bbb

logo

Netrwでファイルを開く時開く先のwindowを指定できるように拡張する。

依存

コード

.vimrc に以下を記述

fu! MyNetrwBrowse(isLocal) let l:wincount = winnr('$') let l:fname = netrw#Call('NetrwGetWord') let l:ischoose = 0 if !(l:fname =~ '/$') if l:wincount > 2 let l:winid = win_getid() call choosewin#start(range(2, l:wincount)) let g:netrw_chgwin = winnr() call win_gotoid(l:winid) en en let l:path = netrw#Call('NetrwBrowseChgDir', a:isLocal, l:fname) if a:isLocal call netrw#LocalBrowseCheck(l:path) else call netrw#Call('NetrwBrowse', 0, l:path) en if g:netrw_chgwin != -1 let g:netrw_chgwin = -1 en endf " override keymappings let g:Netrw_UserMaps = [] call add(g:Netrw_UserMaps, ['<CR>', 'MyNetrwBrowse']) call add(g:Netrw_UserMaps, ['l', 'MyNetrwBrowse']) call add(g:Netrw_UserMaps, ['o', 'MyNetrwBrowse'])

補足

win_getid() を使用しているため Vim 8以上でないと動きません。Vim 7で動かしたい場合は要調整。

scp経由でファイルを編集している時の保存を非同期化する

依存

コード

.vimrc に以下を記述

" inspired by https://github.com/skywind3000/asyncrun.vim/wiki/Get-netrw-using-asyncrun-to-save-remote-files " required: skywind3000/asyncrun.vim fu! s:AsyncSaveRemoteFile(...) abort let l:tmpfile = tempname() sil exe 'w! '.fnameescape(v:cmdarg).' '.fnameescape(tmpfile) let curbufname = expand('%') let scpurm = '^scp://\([^/#:]\+\)\%([#:]\(\d\+\)\)\=/\(.*\)$' let machine = substitute(curbufname,scpurm,'\1','') let port = substitute(curbufname,scpurm,'\2','') let fname = substitute(curbufname,scpurm,'\3','') if port != '' let scpcmd = 'scp -P '.shellescape(port) else let scpcmd = 'scp' endif let escaped_tmpfile = shellescape(tmpfile, 1) let escaped_fname = shellescape(machine.":".fname, 1) let bufnr = expand('<abuf>')+ 0 call asyncrun#run( \ '', \ { 'post': 'call delete('.escaped_tmpfile.')|call setbufvar('.bufnr.',"&modified",0)|echo "Saved. '.curbufname.'"' }, \ scpcmd.' '.escaped_tmpfile.' '.escaped_fname) endfu com! -range=% -nargs=* AsyncSaveRemoteFile call s:AsyncSaveRemoteFile(<f-args>) fu! SetupAsyncSaveRemoteFile() " disable Netrw's BufWriteCmd au! Network BufWriteCmd scp://* aug SetAsyncSaveSCP au! au BufWriteCmd scp://* exe "sil doau BufWritePre ".fnameescape(expand("<amatch>"))|exe "AsyncSaveRemoteFile ".fnameescape(expand("<amatch>"))|exe "sil doau BufWritePost ".fnameescape(expand("<amatch>")) aug END endfu aug AsyncSaveRemoteFile au! au VimEnter * call SetupAsyncSaveRemoteFile() aug END

Wifiアダプタの有効化

mkdir ~/ath10k_backup sudo cp /lib/firmware/ath10k/QCA6174/hw3.0/* ~/ath10k_backup mkdir ~/ath10k_work cd ~/ath10k_work wget https://github.com/linux-surface/ath10k-firmware-override/blob/main/board.bin?raw=true -O board.bin wget https://github.com/kvalo/ath10k-firmware/blob/master/QCA6174/hw3.0/board-2.bin -O board-2.bin # https://github.com/kvalo/ath10k-firmware/tree/master/QCA6174/hw3.0/4.4.1 のうち最新版(最終更新日が若い)をダウンロード wget https://github.com/kvalo/ath10k-firmware/tree/master/QCA6174/hw3.0/4.4.1/firmware-6.bin_WLAN.RM.4.4.1-00282-QCARMSWPZ-1 -O firmware-6.bin sudo cp board.bin /lib/firmware/ath10k/QCA6174/hw3.0/ sudo cp board-2.bin /lib/firmware/ath10k/QCA6174/hw3.0/ sudo cp firmware-6.bin /lib/firmware/ath10k/QCA6174/hw3.0/ sudo chmod +x /lib/firmware/ath10k/QCA6174/hw3.0/* cd rm -rf ~/ath10k_work sudo reboot

説明の簡略化のために wget でダウンロードするコマンドを書いているが、 wget するためには有線でインターネットにつながっている必要がある。有線でつなぐことができない環境の場合は、ほかのインターネットにつながっている端末でファイルをダウンロードし、USBメモリなどで移せばOK。

Linux Surface

インストールした端末(Surface GO)でSDカードのスロットが認識しないなどがあったが、Linux Surfaceを入れたら解決した
https://github.com/linux-surface/linux-surface
手順はWikiを参照
https://github.com/linux-surface/linux-surface/wiki/Installation-and-Setup#debian--ubuntu

ブートローダーの変更

https://github.com/linux-surface/linux-surface/wiki/Surface-Go-2#dual-booting-linux-and-windows

CapsLockをCtrlに置き換え

sudo vi /etc/default/keyboard
# KEYBOARD CONFIGURATION FILE # Consult the keyboard(5) manual page. XKBMODEL="pc105" XKBLAYOUT="jp" XKBVARIANT="" -XKBOPTIONS="" +XKBOPTIONS="ctrl:nocaps" BACKSPACE="guess"

MOZ_USE_XINPUT2

echo export MOZ_USE_XINPUT2=1 | sudo tee /etc/profile.d/use-xinput2.sh

libinput-gestures

https://github.com/bulletmark/libinput-gestures

タイピング中のタッチパッドを無効化

sudo apt remove xserver-xorg-input-synaptics sudo apt install xserver-xorg-input-libinput sudo reboot

gnome-tweaks

sudo apt install gnome-tweaks gnome-tweaks
  • Keyboard & Mouse > Mouse Click Emulation > Fingers > [Check]
  • Keyboard & Mouse > Mouse > Middle Click Paste > [OFF]

Terminalの配色を変更

bash -c "$(wget -qO- https://git.io/vQgMr)"

118(Monokai Pro)に設定

Canvas上に線を引くイベントをtouchmoveにつけると、スマートフォンでピンチイン・ピンチアウトなどをしようとした時に、線が引かれるようになってしまう。
ウェブ開発をしたことある人なら、Canvas外で操作すれば、問題ないことを知っているだろうけど、普通はそういうことには気がつかないことが大半だろうと思われる。
利用者にとってCanvasを使いやすくするためには、その点も配慮したほうが使いやすくなるはずである。

実装例

<div id="wrap"> <canvas id="canvas" /> </div> <style> #wrap { width: 100vw; height: 100vh; position: relative; } #canvas { position: absolute; width: 500px; height: 200px; top: 0; bottom: 0; right: 0; left: 0; margin: auto; } </style>
const canvas = document.getElementById('canvas'); const canvasWrapper = document.getElementById('wrap'); // @var {Bool} isDrawing 描画可能か let isDrawing = false; canvasWrapper.addEventListener('touchstart', (event) => { if ( event.touches.length === 1 && document.elementFromPoint(event.touches[0].clientX, event.touches[0].clientY) === canvas ) { isDrawing = true; } else { // canvasWrapperとcanvasの境目辺りをタッチした場合、稀にスクロールイベントが先に発火してしまうことがある。 // それを抑制するために、イベントの伝播を止める event.stopPropagation(); isDrawing = false; } }); canvas.addEventListener('touchmove', (event) => { if (!isDrawing) { return; } event.preventDefault(); // ここに描画処理 });

touchstart イベントは、canvasではなく親要素に付けているところがポイント。

CakePHPにおいてモデルの各イベント間で値を共有したい場面がたまにある。
たとえば、 beforeSave でしか取得できない値を afterSave で使いたいとか、エンティティの値によって buildRules のルールに値を渡したいなど。

そういう場合、以下の方法で値の受け渡しができるようになる:

// モデル内で public function beforeSave(Event $event, EntityInterface $entity, ArrayObject $options) { $options['hogehoge'] = 'hugahuga'; } public function afterSave(Event $event, EntityInterface $entity, ArrayObject $options) { debug($options['hogehoge']); // => 'hugahuga'; }

各イベントの $options はイベント間で同じものが使いまわされているため、先に発生するイベントで追加された値は、後のイベントで利用できるようになる。
上記の例では、 beforeSave で追加した $options['hogehoge']afterSave で利用できるようになっている。

また、この $optionsルールチェッカー$rules->add の引数としても利用されているため、そこでも利用できる。

public function beforeSave(Event $event, EntityInterface $entity, ArrayObject $options) { $options['foo'] = true; } public function buildRules(RulesChecker $rules) { $rules->add(function ($entity, $options) { debug($options['foo']); // => true; }, 'ruleName'); return $rules; }

上記の例では、単純に固定値を設定しているだけだが、複数のルールで同じモデルの同じレコードを参照しないといけない場合など、あらかじめ beforeSave などで、その共通で参照しないといけないデータを取得して $options に追加しておけば、ルール毎にfindする必要がなくなり、多少は高速化が見込める。
同じコードを何度も書く必要がなくなり、見通しがよくなるはず。

idnameemail
1hogeexample.hoge@example.com
2hugaexample.huga@example.com
3piyoexample.piyo@example.com

このようなテーブル( users とする)があった時、次のコードで id を一次元配列で一発で取得できる:

$result = TableRegistry::getTableLocator()->get('Users')->find()->select('Users.id')->extract('id')->toList(); // $result = [1, 2, 3]

email だけを取得したい場合も同様:

$result = TableRegistry::getTableLocator()->get('Users')->find()->select('Users.email')->extract('email')->toList(); // $result = ['example.hoge@example.com', 'example.huga@example.com', 'example.piyo@example.com']

このコードは CakePHP3 以上であれば動くはず

スマホやタブレットでタッチ操作をしている場合、 DragEvent が使えないため、 TouchEvent を利用して擬似的にドラッグ&ドロップを実現する必要がある。
本記事はそのヒント。

まずはじめに、ドロップイベントを実現するには、 touchend イベントと document.elementFromPoint を組み合わせればよい。 touchend イベントが発火した場所の要素を document.elementFromPoint で取得し、その取得できた要素がなにかを判定して、すきな処理を行えばよい。

例:

/** * @param {Event} event イベントオブジェクト * @return {Element|null} */ const getElementFromEventPoint = (event) => { const { clientX, clientY } = /^touch/.test(event.type) ? event.changedTouches[0] // touchstart|touchmove|touchendの場合はこちらから座標を取得 : event; // その他(マウスやポインタ)のイベントの場合は、こちらから座標を取得 return document.elementFromPoint(clientX, clientY); // 座標から要素を取得 } let isDragging = false; window.addEventListener('touchstart', () => { isDragging = true; }); window.addEventListener('touchend', (event) => { if (!isDragging) { return; } isDragging = false; const el = getElementFromEventPoint(event); // イベントが発生した座標の要素に `data-drop-zone` 属性が付与されていたらなにかしら処理する if (el && typeof el.dataset.dropZone !== 'undefined') { // 処理... } });

この例では、 touchend でドロップを再現しているけれど、同じ要領で touchmovedragover を再現したりすることが可能。

https://codepen.io/riffrain/pen/yLLWYgq の様に、よくあるドラッグ&ドロップを簡単にするパッケージと組み合わせたり、それの拡張( https://codepen.io/riffrain/pen/yLLWYgq では、本来ドロップできない外の要素でドロップを検知している)をすることができる

vimfilerからNERDTreeに乗り換えた時、ファイルツリーでの操作でどうしても馴染めなかった箇所をvimfilerのような動きをするように変更
ファイルを開く際にウィンドウを選択させるために、 vim-choosewin を利用

[[plugins]] repo='t9md/vim-choosewin' hook_add=''' let g:choosewin_label='sdfghjkl' let g:choosewin_overlay_enable = 1 let g:choosewin_overlay_clear_multibyte = 1 ''' [[plugins]] repo='preservim/nerdtree' depends=['vim-choosewin'] hook_add=''' let g:NERDTreeMouseMode = 0 let g:NERDTreeMapToggleHidden = '.' let g:NERDTreeMapCustomOpen = 'o' let g:NERDTreeMapActivateNode = '<CR>' nn <Leader>e :NERDTreeToggle<CR> ''' hook_post_source=''' cal NERDTreeAddKeyMap({ 'key': 'l', 'callback': 'OpenCurrentDir', 'scope': 'DirNode', 'quickhelpText': 'Open current dir' }) cal NERDTreeAddKeyMap({ 'key': 'h', 'callback': 'CloseCurrentDir', 'scope': 'Node', 'quickhelpText': 'Close current dir' }) cal NERDTreeAddKeyMap({ 'key': g:NERDTreeMapActivateNode, 'callback': 'ChooseWinOpen', 'scope': 'FileNode', 'override': 1 }) fu! OpenCurrentDir(dirNode) abort if !a:dirNode.isRoot() && !a:dirNode.isOpen cal a:dirNode.activate({}) let l:children = a:dirNode.getVisibleChildren() cal l:children[0].putCursorHere(1, 0) en endf fu! CloseCurrentDir(node) abort let l:node = a:node.path.isDirectory ? a:node.getCascadeRoot() : a:node if !l:node.isRoot() && !empty(l:node.parent) && !l:node.parent.isRoot() && l:node.parent.isOpen cal l:node.parent.putCursorHere(1, 0) cal l:node.parent.activate({}) en endf fu! ChooseWinOpen(fileNode) abort let l:wincount = winnr('$') if l:wincount > 2 let l:nardwinnr = winnr() cal choosewin#start(range(2, l:wincount)) silent exe l:nardwinnr.'wincmd w' en cal a:fileNode.activate({ 'reuse': 'all', 'where': 'p', 'keepopen': 1 }) endf '''

※ 諸事情でいまだにvim7を利用する場合があるので、vim7でも動くような実装になっています。

プラグイン管理は dein.vim を利用しています。
NERDTreeAddKey を実行するとき、 hook_post_source でしかうまく起動時に追加されなくて、 post_source を使うために、別途 .vimrc には au VimEnter * cal dein#call_hook('post_source') を追加してます。
※ 上記はtomlファイルの中の内容を一部抜粋したものです。
※ 新しいプラグインを追加した時に、毎回 NERDTreeAddKey でエラーになりますが、再起動すればいいだけなのでスルー気味