basyura's blog

あしたになったらほんきだす。

asyncomplete.vim に乗り換えを試みる

ずっと neocomplete をfork して一部いじったものを使っていて、不満は無くはないけど使えてるのでまあいいかで今まで来たのだけど試しに乗り換えを試みてみる。第一候補の asyncomplete.vim を触ってみると autopopup はいいのだけど、enter 押したら第一候補を確定してほしいけどしてくれないとか、ポップアップメニューのチラツキが多いのが気になるとかあって久々に Vim script してみた。

inoremap <expr> <cr> pumvisible() ? <SID>decide() : "\<cr>"

function! s:decide()
  return "\<C-n>\<C-c>a"
endfunction

let s:min_chars = 2
let s:popup_delay = 200

augroup MyAllAsyncompleteStting
  autocmd!
  autocmd BufEnter *  call s:all_settings()
augroup END

let s:settings = {
  \ 'html' : {'min_chars': 2, 'popup_delay': 50},
\}

function! s:all_settings()
  let setting = {'min_chars': s:min_chars, 'popup_delay': s:popup_delay}
  if has_key(s:settings, &filetype)
    let setting = s:settings[&filetype]
  end

  let g:asyncomplete_min_chars = setting.min_chars
  let g:asyncomplete_popup_delay = setting.popup_delay
endfunction

let s:before = {'len': 0, 'word': ''}
function! s:my_asyncomplete_preprocessor(options, matches) abort
  let l:visited = {}
  let l:items = []
  for [l:source_name, l:matches] in items(a:matches)
    let l:startcol = l:matches['startcol']
    let l:base = a:options['typed'][l:startcol - 1:]
    for l:item in l:matches['items']
      for l:item in matchfuzzypos(l:matches['items'], l:base, {'key':'word'})[0]
        if has_key(l:visited, l:item.word)
          continue
        end
        call add(l:items, s:strip_pair_characters(l:base, l:item))
        let l:visited[l:item.word] = 1
      endfor
    endfor
  endfor

  if len(l:items) == 0
    call asyncomplete#preprocess_complete(a:options, l:items)
    let s:before = {'len': 0, 'word': ''}
    return
  end

  if s:before.len != len(l:items) && s:before.word != l:items[0].word
    call asyncomplete#preprocess_complete(a:options, l:items)
  endif

  let s:before = {'len': len(l:items), "word": l:items[0].word}
endfunction

let s:pair = {
\  '"':  '"',
\  '''':  '''',
\}
function! s:strip_pair_characters(base, item) abort
  " Strip pair characters. If pre-typed text is '"', candidates
  " should have '"' suffix.
  let l:item = a:item
  if has_key(s:pair, a:base[0])
    let [l:lhs, l:rhs, l:str] = [a:base[0], s:pair[a:base[0]], l:item['word']]
    if len(l:str) > 1 && l:str[0] ==# l:lhs && l:str[-1:] ==# l:rhs
      let l:item = extend({}, l:item)
      let l:item['word'] = l:str[:-2]
    endif
  endif
  return l:item
endfunction

let g:asyncomplete_preprocessor = [function('s:my_asyncomplete_preprocessor')]



call asyncomplete#register_source(asyncomplete#sources#buffer#get_source_options({
      \ 'name': 'buffer',
      \ 'allowlist': ['ruby', 'html'],
      \ 'blocklist': [],
      \ 'completor': function('asyncomplete#sources#buffer#completor'),
      \ 'config': {
        \    'max_buffer_size': 5000000,
        \  },
        \ }))

call asyncomplete#register_source(asyncomplete#sources#file#get_source_options({
      \ 'name': 'file',
      \ 'allowlist': ['*'],
      \ 'priority': 10,
      \ 'completor': function('asyncomplete#sources#file#completor')
      \ }))

call asyncomplete#register_source(asyncomplete#sources#neosnippet#get_source_options({
      \ 'name': 'neosnippet',
      \ 'allowlist': ['*'],
      \ 'blocklist': [],
      \ 'priority': 100,
      \ 'completor': function('asyncomplete#sources#neosnippet#completor'),
      \ }))

自分向けにいい感じなったとも言えるが・・・様子を見てみる。

inkdrop - 未完了のタスクにジャンプする

Vim plugin を入れている状態で、タスク * [ ] にジャンプする

inkdrop.commands.add(document.body, "mycmd:find-task", () => {
  const vim = inkdrop.packages.activePackages.vim.mainModule.vim;
  vim.getVimGlobalState().query = /\[ \]/;

  const el = inkdrop.getActiveEditor().cm.getWrapperElement();
  inkdrop.commands.dispatch(el, "vim:repeat-search");
});

もっといい方法がありそうだけど。

iPhone SE → iPhone 12 mini

iPhone SE

iPhone SE にしたのが 2016/05/14 で、同時に au から IIJ (BIC SIM) に乗り換え。それから今日までにバッテリー交換とディスプレイ交換を一回ずつ。 ディスプレイ交換したのはディスプレイが "少し" 浮き上がっていたからで、そろそろサポートされなくなりそうだけどもうちょっと使い倒したいと思って交換。 今回はディスプレイがモリモリに浮き上がっていたからで原因は発熱と思われる。もっと言うとドラクエウォーク起因。めちゃくちゃ発熱する。熱くはなるもののそれ以外に問題を感じなかったので特に冷やしたりもせず暑い時期に歩き回ったのがダメだった。

iPhone 12 mini

(amazon で本体は扱ってないのかな?)

iPhone 5s 以降ははサイズがでかいし高いしで SE を乗り越えながら長期間使い倒していこうと思っていたのだけど、SE 2 より 12 mini の方がサイズが小さかったので採用 (値段は高いけど・・・)。SE と同様に 5 年使うとして、バッテリ交換や修理が発生したとしてトータル 10 万とすると 1 年あたり 2 万というのは・・・ちょっと高く感じてしまうなぁ。

移行

iCloud のバックアップから戻したのとソフトウェアアップデートの発動でやや時間がかかったものの大きな問題はなく終了。SIM カードを入れ替えるだけで電話はつながったし、もろもろのアプリのログイン状態復帰は 1Password 経由で一瞬だった。困ったのは Overcast のリストがうまく同期されなかったぐらい。会社で必要な MS Authenticator は休み明けに対応が必要 (Google Authenticator は QR コード読み込むだけでかんたんに引き継げたのに)。

つかいごこち

まだよくわからないけど face ID は良さそう。 指紋認証も便利だったけど認識してくれない率が高すぎて毎回パスコード入れてたのでちょっと辛いなとは薄々思っていた。それに比べると iPhone を持ち上げるだけで済むのはかなり便利。

まとめ

ドラクエウォーク再開。

inkdrop - ウインドウタイトルをクリアする

Windows 版だとウインドウタイトルに記事のタイトルが出るようになった。チラつくのが気になるのでクリアするようにしてみる。

init.js

inkdrop.onEditorLoad(() => {
  const ele = document.querySelector(".editor-header-title-input input");
  const observer = new MutationObserver((_) => {
    setTimeout(() => {
      inkdrop.window.setTitle("");
    }, 300);
  });
  observer.observe(ele, {
    attributes: true,
  });
  setInterval(() => {
    inkdrop.window.setTitle("");
  }, 3000);
});

記事切替だけではなく編集中にもタイトルが書き換わるので定期的に実行するようにした。

inkdrop - active なノートを表示する

複数 book 運用で詳細表示してるときの状態を判定するのに queryContext だと不都合が多かったので sidebar.workspace を使うように改良。

inkdrop.commands.add(document.body, "mycmd:select-active", () => {
  const { sidebar } = inkdrop.store.getState();

  const status = "active";
  const bookId = sidebar.workspace.bookId;
  if (bookId != "") {
    inkdrop.commands.dispatch(
      document.body,
      "core:note-list-show-notes-in-book",
      { bookId, status }
    );
  } else {
    inkdrop.commands.dispatch(
      document.body,
      "core:note-list-show-notes-with-status",
      { status }
    );
  }
  inkdrop.commands.dispatch(document.body, "editor:focus");
});

inkdrop - 複数の book を使ってみる

sidebar が溢れてきた

inkdrop を使い始めて 1 年半経過。

f:id:basyura:20210523193438p:plain

タグが増えてきて Status を閉じてもスクロールバーが発生するようになったので css で消した。

.sidebar-menu::-webkit-scrollbar {
  display: none;
}

Tags の下だけスクロールバーが出せるようになるといいけど HTML 構造的にできなさそうなのでガッツリ消した。気にならなくなった。タグの / 区切りを階層構造で表現できて開閉できるようになると表示領域を圧縮できて便利そうだけど開閉の手間もあるので悩ましい。

複数 book 運用

今まで 1 book 運用をしていたのだけど、複数 book をお試し中。 book は階層構造で分類しやすいし辿りやすい点はあるけどタグで横串に見れて便利。book を行き来するのも効率悪くて悩む。

switch-notebook を使ってみたのだけど book の選択 (UI で detail を選択したときの挙動) まではできてなくて、api (command) 的にも用意されてなさそうなのでちょっと微妙。

自作 switch-notebook

しかたないので自作してみた。document.querySelectorAll などを使って画面上の要素をとって一覧表示して、選択時はその要素をクリックして画面操作を再現するゴリゴリ仕様。

keymap.cson

'ctrl-x ctrl-n': 'switch-notebook:open'

f:id:basyura:20210523193539p:plain

割と便利。fork したけど中身が全く別名で plugin 登録してもいいけどこの手の plugin の挙動は個人の趣味に左右されることが多いので悩ましい。

Active Status なノートを表示する

キーボード中心の操作をするので、アクティブなステータスを表示する際の自作コマンドも見直し。 book が選択されているかそうでないかで挙動を切り替える。

init.js

inkdrop.commands.add(document.body, "mycmd:select-active", () => {
  const { queryContext } = inkdrop.store.getState();

  if (queryContext.mode == "book") {
    inkdrop.commands.dispatch(
      document.body,
      "core:note-list-show-notes-in-book",
      {
        bookId: queryContext.bookId,
        status: "active",
      }
    );
  } else {
    inkdrop.commands.dispatch(
      document.body,
      "core:note-list-show-notes-with-status",
      {
        status: "active",
      }
    );
  }
  inkdrop.commands.dispatch(document.body, "editor:focus");
});

inkdrop.commands.add(document.body, "mycmd:select-all-notes", () => {
  const { queryContext } = inkdrop.store.getState();
  if (queryContext.mode == "book") {
    inkdrop.commands.dispatch(
      document.body,
      "core:note-list-show-notes-in-book",
      {
        bookId: queryContext.bookId,
      }
    );
  } else {
    const node = document.querySelector(".sidebar-menu-item-all-notes");
    node.querySelector(".content").click();
  }
});

keymap.cson

'ctrl-@': 'mycmd:select-active'
'ctrl-x ctrl-@': 'mycmd:select-all-notes'

まとめ

また一つ便利になった。