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
Get-AppxPackage Microsoft.SecHealthUI -AllUsers | Reset-AppxPackage
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。
インストールした端末(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
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"
echo export MOZ_USE_XINPUT2=1 | sudo tee /etc/profile.d/use-xinput2.sh
https://github.com/bulletmark/libinput-gestures
sudo apt remove xserver-xorg-input-synaptics sudo apt install xserver-xorg-input-libinput sudo reboot
sudo apt install gnome-tweaks gnome-tweaks
bash -c "$(wget -qO- https://git.io/vQgMr)"
118(Monokai Pro)に設定
.no-scrollbar { scrollbar-width: none; } .no-scrollbar::-webkit-scrollbar { display: none; }
https://developer.mozilla.org/en-US/docs/Web/CSS/::-webkit-scrollbar
https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-width
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する必要がなくなり、多少は高速化が見込める。
同じコードを何度も書く必要がなくなり、見通しがよくなるはず。
id | name | |
---|---|---|
1 | hoge | example.hoge@example.com |
2 | huga | example.huga@example.com |
3 | piyo | example.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
でドロップを再現しているけれど、同じ要領で touchmove
で dragover
を再現したりすることが可能。
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
でエラーになりますが、再起動すればいいだけなのでスルー気味