Emacsのlsp-modeが遅いときはlsp-doctorを見てみるといい

emacs-lsp.github.io

タイトルと↑のリンクでFAなのだけれど、EmacsのLSPクライアントであるlsp-modeで補完などの動作が遅いなと感じたときは M-x lsp-doctor とやると遅い原因を列挙してくれる。

Emacsのデフォルト設定値ではlsp-modeの利用に最適ではない場合があるのは、言われてみればその通りなのだけど完全に失念していた。 lsp-mode側でよしなに書き換えてしまってもいいのに……と思ったけど、それをやるのは流石に思想の違いがありそう。

自分の場合はGCが発生するメモリの閾値gc-cons-threshold と、一度の読み取り操作でサブプロセスから何バイト読み取るかの read-process-output-max の値を調節すると、補完候補が出る速度が体感でわかるほど早くなった。 元々候補が出るのに一瞬ひっかかるなと感じる程だったので、明らかに体験が良くなった。

lsp-mode 遅いなと感じている時は試してみるとよさそう。

※ 追記 v7.0 までは lsp-diagnose って名前だったらしい

github.com

ページのコンテンツから離れたタイミングのブラウザイベントの選び方

これは はてなエンジニアAdvent Calendar 2020 1日目のエントリーです。

ブラウザに何か表示させるアプリケーションを作っている人は、ブラウザのAPIやイベントの発火タイミングについて調べる機会が年に何度かあると思います。今回はそんな調査の一つを紹介です。

知りたいこと

ユーザーがページから離れたタイミングを取れるイベントが知りたい。

  • ユーザーがページから離れたら際に行いたい処理があったので、契機となるイベントがほしい
  • ここでは「ページから離れた = ページのコンテンツを見ていないと想定される」とする。具体的には以下のようなタイミング
    • ページ遷移
    • タブ移動
    • ページ破棄 (タブ/ブラウザを閉じる)
    • 画面がロックされる
    • 別のアプリケーションに切り替える (PCなら画面の最小化/モバイルならアプリ切り替え)
  • beforeunloadイベントでタブやブラウザ自体を閉じる際に処理を挟めるのは知っていたが、タブの切り替えやPC/モバイルの画面がロックされた場合にも処理を行いたいので、何を使えばいいのか知りたい

結論

  • visibilitychangeイベントで「ページから離れた」のユースケース全てを満せるので、このイベントを契機に処理を行う
  • Safariではバグがあるので、一部pagehideイベントと合せて実現する
  • (未検証)IEではページ遷移時の処理が途中で打ち切られているような挙動をしているので、IEをサポートするのならbeforeunloadも合せて実現する

以下この結論に至った過程を紹介。

調査と検討過程

基本方針

改めて対応したいことは「ページから離れた = ページのコンテンツを見ていないと想定される」タイミングで発火されるイベントが何かということ。

このケースは全てPage Visibility APIで対応できる。

developer.mozilla.org

visibilitychangeイベントが発火し、 visibilityState === 'hidden' であれば「ページから離れた」と判断できるので、このタイミングで処理を行えば良い。 Can I Useを見る限り主要ブラウザではサポートされているので、気にせず使っていけば良さそう。

ただしSafariにバグがある。

Safari向け対応

Can I UseのSafariの注釈[3]を読むと、こう書いてある。

https://caniuse.com/mdn-api_document_visibilitychange_event

Doesn't fire the visibilitychange event when navigating away from a document, so also include code to check for the pagehide event (which does fire for that case in all current browsers)

(意訳) visibilitychangeがページ遷移時に発火しないので、このタイミングではpagehideを利用する必要がある

そんな……。

ページ遷移時だけを考えるならpagehideのみを使えばよいが、今回はページから離れた時全般なので、visibilitychangeとpagehideのそれぞれで処理を行う必要がある。

このときvisibilitychangeとpagehideの両方に処理を登録すると、ページ遷移時に2回処理が実行されてしまうので、Safariのみpagehideイベントに行いたい処理を登録する必要がある*1

このSafariのバグについて、WebKit Bugzilla見てみると 2020/09/25 時点で更新がある。もしかしたら近いリリースでの修正が期待できるかもしれない。

IEの挙動について

IEではページ遷移時にvisibilitychangeやpagehideのイベントが発火はしているものの、処理が終わり切らず途中で終わっているような挙動をしているように見えるため、代替手段が必要になる。挙動は深く追ってはいないものの、beforeunloadなら処理を最後まで終了させているようなので、IEをサポートする必要があるならこれを利用する必要もあるだろう*2

以上をまとめると以下のようなコードになる。

const doSomething = () => { /* ページのコンテンツから離れた際の処理 */ };

document.addEventLisnter('visibilitychange', () => {
  const state = document.visibilityState;
  if (state === 'hidden') {
    doSomething();
  }
});

if (isSafari()) {
  window.addEventListener('pagehide', () => {
    doSomething();
  });
}

if (isIE()) {
  window.addEventListener('beforeunload', () => {
    doSomething();
  });
}

調査過程で得た情報

調査の過程で得た、他にも応用できそうな情報を上げておく。

Page Lifecycle API

developers.google.com

ページが開かれてから閉じるまでの状態やイベントの遷移をまとめたもの。

これを読んでおけばページの状態にまつわる処理を書きたくなった時にサッと参照出来てお得。ただしPage Lifecycle API自体はChromeでしか実装されていなさそうなので、あくまで参考程度に止めた方がよさそう。

ライフサイクルをまとめた図を見ると、モバイルのネイティブアプリのライフサイクルと似たイメージなのかなとも思ったりする。

またbeforeunloadで何か処理を行うのは、ページ離脱時のユーザー体験を損ねる可能性があるので、保存前の変更を警告するため だけ に使うべきなど、Legacy Lifecycle APIの節で言及されており、勉強になる。

デバッグ便利情報

event-logger.glitch.me

WebKit BugzillaのIssueコメントにあった、動作確認用のglitch。visibilitychangeやpageshow/hideの発火毎にログを表示してくれるもので、今回のAPIの挙動理解の助けになった。

また実装がデバッグの手段の一つの参考になる。素朴なイベントの動作確認方法だと、イベント発火時にconsole.debugでコンソールに出力させるのがあるけど、今回は発火のタイミングがタブやブラウザを閉じるまで含まれるのでコンソールから消えてしまう。このglitchではローカルストレージにイベント発火のログを残しており、タブやブラウザを閉じても消えないようにしている。

ローカルストレージに残す発想は考えてみたら当たり前ではあるが、使ったことの無い手法なので参考になる。

まとめ

ページのコンテンツから離れたタイミングのイベントの選び方とその検討過程について紹介した。Page Visibility APIやライフサイクルの図は覚えておくと便利なタイミングがありそう。

ほしいブラウザのAPIを調べるのが最初だったけれど、ページのライフサイクルだったりデバッグテクニックまで知れてお得な題材だった。

明日は id:nabeop さんです!

*1:ブラウザによる条件分岐を避けたいのなら、行いたい処理をべき等にしたり、処理が実行されたらフラグを立て1度しか実行されないようにするなども考えられる。

*2:同僚の id:koudenpa さんが挙動回りを確認してくれた。

IntelliJ を使って Pull Request をレビューする

言いたいことはタイトルが全て。

IntelliJ には GitHub と連携して Issue や Pull Request を見れる機能があり、しっかりコードレビューする際は GitHub のサイト上ではなく IntelliJ 上で行うようにしている。この IntelliJ でレビューを行うことはサイト上で行うよりも体験が良く、便利に思っていた。使い始めた頃は Pull Request の一覧やコメントを書く程度だったが、先日のバージョン2020.2のアップデートでついにレビュー関連の機能が完全にサポートされたので、これを機に IntelliJ 上でレビューを行う体験の何が良いかについて紹介したい。

blog.jetbrains.com

便利だと感じている点は4つ。以下説明していく。

  • 手元に Checkout せずに済むこと
  • 定義/参照ジャンプなどの IntelliJ の機能が使えること
  • IntelliJ の 差分表示が見やすい
  • IntelliJ上でレビュー作業の全てが完結すること

手元に Checkout せずに済むこと

Checkout せずに済むことは言葉の通りで、GitHub 上で見える差分以上に見たくなった時に手元に Checkout する場面はあると思う。この時始めから IntelliJ で Pull Request を見ていれば Checkout のコマンドを打つひと手間無く差分の周辺コードを見に行ける。

最近は GitHub CLI で簡単に Checkout できるようになっているので、この点はあまり推す点でもなくなってきているのかもしれない*1

定義/参照ジャンプなどの IntelliJ の機能が使えること

一押しはこれ。IntelliJ 上で差分を見れることで、エディタ上で未使用変数・スペルミスのハイライト、コードジャンプといった機能を使いコードを見ていけるのが便利に感じている。 GitHub 上だけでレビューしていると未使用変数やスペルミスは見落としがちで、この点は非常に助かっている。コードジャンプも GitHub 上だとブラウザの検索でメソッド名を探すといった頑張りをしなければいけなかったことが、普段のコードを読むのと同じ体験で見ていけるのは大きなメリットだと思う。

ここに書いたメリットを GitHub 上でも得られるようになってきている*2*3が、普段仕事で書いている Perl はこれらのメリットが得られにくい。この点 IntelliJPerl プラグインは優秀で、ここで書いた機能は全て提供されている。詳しくは以前書いたエントリを参照してもらいたい。

benevolent0505.hatenablog.com

差分表示が見やすい

機能の話ではあるが差分表示の見やすさは特に紹介したい。個人的に GitHub の差分表示はもっとも見やすいとは言いづらく、もっと良くなってほしいと思う点だった。 IntelliJ の差分表示は見やすく、どこに何が追加・削除されたのか、差分の周辺はどうなっているのかが見やすいと感じている。文章で伝えにくい点なので、ヘルプは画像検索の結果を見てもらいたい。

IntelliJ上でレビュー作業の全てが完結すること

Checkout の話にも通じるが、ブラウザとエディタの行き来が無くなり IntelliJ だけでレビュー作業の全てが完結できるのは、開発の効率化という面で大きなメリットだと思う。 この話は単に手間が減ることだけでなく IntelliJ (IDE) が開発の起点になるということ だと思う。別に IntelliJ が無くてもレビューや他の開発は出来るが、IntelliJ にまとめ上げることで作業を集中させ、開発を行いやすくできるのではないかと思う。

IDE とはなんなのかという話については別の方がまとめており、なるほどなと思ったのでそちらを紹介しておく。

irof.hateblo.jp

まとめ

IntelliJ で Pull Request をコードレビューするのが便利だと思う点について書いた。普段使っているツール上でレビューできる良さはやってみないとイメージしづらいものも多いと思うので、既に IntelliJ を使っている人は試してみると良いと思う *4。 また IDE が開発の起点になることに軽く触れた。元々 IntelliJ をデフォルトでコーディング支援の整ったエディタ程度にしか思っていなかったが、レビュー関連の機能を使ってからは IntelliJIDE であるということを意識するようになった。IDE は開発を行いやすくするツールであることは今後も意識していきたいと思う。

cspell でレポジトリの typo 判定一覧を出力する

Pull Request のレビューなどで typo を指摘することがあるだろうが、typo の指摘をしてもマイナスをゼロにするレビューコメントが一つ増えるだけであまり嬉しくない。

こういった無用なレビューを避けるために手元のエディタでスペルチェッカーが動かしている場合も多いと思う。最近なら多くのチームメンバーが VSCode を使っていて、レポジトリに .vscode を追加するだけで推奨拡張機能を皆のエディタに入れられるようになったので大変便利*1

Code Spell Checker - Visual Studio Marketplace

しかしこういったスペルチェッカーは造語や辞書に登録されていない単語を検知してしまうので、我々にとって誤検知であることが多い。 Code Spell Checker は設定ファイルに allowlist を記述でき、typo 判定してほしくないキーワードを登録できる。 「よし、これで導入だ」といきたいところだが、せっかくならキーワードの登録も同時に行ってしまいたい。その時に試したやり方を残しておく。

cspell を使う

Code Spell Checker は内部で cspell というライブラリを使っていて、npm でコマンドラインツールとしても配布されている。元々この拡張のために作ったツールをコマンドラインでも使えるように提供しているらしい。

www.npmjs.com

使い方は素朴でディレクトリを指定したりファイル名を直接指定すれば OK 。今回は一度単語リストが出せれば良いので npx 経由で使ってみた。

$ npx cspell <filename>

あとはお好みに合せてファイル名を指定すれば完了。 git レポジトリだったら git ls-files とかで拡張子を指定しつつファイル一覧を取得すると楽だと思う。例えば Perl だったらこんな感じになる。

#!/bin/sh

files=$(git ls-files '*.pm' '*.pl' '*.t')
exec npx --quiet cspell --wordsOnly --unique --no-summary $files

単語の一覧だけ出したいので --wordsOnly--no-summary, --unique を指定した。このままだと npx の出力も出てしまうので、 --quiet を付けると良い。

「よしよし、これで」と思ったが、普段触っているレポジトリで試してみると結構な数の語が検出されてしまい、全部 allowlist に載せるのは厳しい。もちろん typo がたくさんあるわけではなくて、 subtest などのライブラリ特有のキーワードが typo 判定されてしまったため。頻出するキーワードが typo 判定されてたらイライラしてコードの質が下がることになりかねないと思う()。

そこで作戦を変えて、頻出語順に出力し上位20件程度をリストに加えるやり方にした。

#!/bin/sh

files=$(git ls-files '*.pm' '*.pl' '*.t')
exec npx --quiet cspell --wordsOnly --no-summary $files | sort | uniq -c | sort -nr

--unique を指定するのは止め、昔ながらの UNIX コマンドの組み合わせで頻出語を出すようにした。これで allowlist に登録すると良いキーワードを判別しやすく、理由を持って追加できるようになった。

実例

試しにインターン課題のサンプルアプリケーション go-Intern-Bookmark でリストを出してみる。

~/repos/src/github.com/hatena/go-Intern-Bookmark
$ ./spellcheck.sh
  43 hatena
  13 graphql
  12 sqlx
   7 stretchr
   7 signup
   6 signin
   6 csrf
   4 tmpl
   4 jmoiron
   4 bcrypt
   4 Tmpl
   3 nosurf
   3 graphiql
   3 Signup
   3 Signin
   3 Queryx
   3 Graphql
   2 entrys
   2 attatch
   1 unmatching
   1 justinas
   1 fmsec
   1 dimfeld
   1 UUID
   1 CSRF

コード量が少ないためかおびただしい件数が検出されることはなかった。 GraphQL みたいな技術用語を検出するのは結構予想通りだが Hatena はなるほどという気持ちになった。 Go だとレポジトリ名がモジュール名に含められるので、だいたいこうなってしまいそう。

まとめ

cspell でレポジトリの typo 判定一覧を出す方法を書いた。 cspell 意外に便利そうで、 README にあるように Git の commit-hook や CI でチェックするようにしても効果的だと思う。

参考

*1:ただ僕はIntelliJEmacsを使っている

君はあのコンマ演算子を知っているか?

僕は知らなかったです。

Perlで遭遇

次のようなPerlのコードを読んでいるときに思ってなかった挙動をした。

my $x = ('a', 'b');
$x  # => ??

始め以下のような想定をして $x には 'a' が入るのだと思っていた。

  1. 左辺がスカラコンテキストなので、右辺もスカラコンテキストになる
  2. 配列の区切りの最初の値が入り $x には 'a' が入る
  3. 'b' はどの変数にも入らず捨てられる

しかし実行してみると $x には 'b' が入る。

my $x = ('a', 'b');
$x  # => 'b'

これはどういうことかと思い調べてみると、どうやらコンマ演算子というものがあるらしい。perldocを引いてみると

perldoc.jp

二項演算子の "," はコンマ演算子です。 スカラコンテキストではその左引数を評価し、その値を捨てて、 それから右引数を評価し、その値を返します。 これはちょうど、C のコンマ演算子と同じです。 リストコンテキストでは、これは単にリスト引数の区切り文字で、 双方の引数をそのリストに挿入する働きがあります。 これらの引数も左から右に評価されます。

そんなのあるの……。つまりさっきのコードは以下のような流れで 'b' が入る。

  1. 左辺がスカラコンテキストなので、右辺もスカラコンテキストになる
  2. 'a', 'b',二項演算子コンマ演算子として働く
  3. そのため 'a' を評価し値を捨て、 'b' を評価し値を返す
  4. そして $x には 'b' が入る

という流れ。

恥ずかしながら2年弱Perlを書いててコンマ演算子のことは初めて知った……。あまりこういうコードを書く機会は無いだろうけどミスってこのようなコードを書いてしまった時は意図しないハマりポイントになりそうだなと思った。

ちょっと待てよ……

Perlのハマりポイントを知ったところで、さっきのperldocの文を思い出してみると……。

これはちょうど、C のコンマ演算子と同じです。

ということはC言語やそれ以外の言語にもあるということではないだろうか?そう思って雑にググってみるとWikipediaがヒットした。

コンマ演算子 - Wikipedia

CやC++をめきめき書いたことは無いのでなるほど〜といった感想程度だったけど、次のコード例はちょっと面白い。

int a=1, b=2, c=3, i;   // このコンマは演算子としてではなくセパレータとして作用する
i = (a += 2, a + b);    // aに2を加算した後、a+b = 3+2 をiへ代入する                                    ... a=3, b=2, c=3, i=5

ちょっとしたコード短縮テクニックなのかなと思ったけど、Twitterで調べてみたら定期的にツイートがある。CやC++ということは競技プログラミングの界隈だと普通に知られたテクニックだったりするのだろうか?

コンマ演算子 - Twitter Search

お前もか〜〜

見つけてしまった瞬間「マジかよ……」と思ってしまったのだけれ、JavaScriptにもコンマ演算子は存在していた。雰囲気は同じ感じで詳細はMDNを見ればよさそう。

developer.mozilla.org

Minifierとかでコードが圧縮された後だとこんな感じのコードが生成されているのかもしれないなと思った。

終わりに

知らない演算子を発見するの結構新鮮な気分だった。PerlJavaScriptも仕事で書いているけど、言語の仕様を上から下までさらって覚えるということはしたことないので他にもこういった発見は今後もあるんだろうなと思った。 (あとJavaScriptオチ担当として優秀な気がする)

iTerm2をやめてTerminal.appを使うようにした

タイトル通りです。

Macを使い始めてからずっとターミナルエニュレータとしてiTerm2を使ってきたのだけど、iTerm2じゃなくても別によいなと思ったので始めから入っているTerminal.appを使い始めた。 前からiTerm2じゃなくていいなと思ってたけど止めようと思う決定的な理由も無くてダラダラ使い続けていた。しかし先日のiTerm2の脆弱性の原因を見て、tmux統合の機能使ってないのに巻き込まれみたいな感覚になるのちょっとな……と思ったので止めることにした。

iTerm2を止める理由もTerminal.appにする理由もそこまで無くて、挙げてもこれくらい。

  • iTerm2は色々機能があるみたいだけど全然使ってない
  • tmux統合機能があるけど、自分はscreenを使っているので必要無い
  • むしろ設定することがそんなにないTerminal.appの方が良いのでは?

Terminal.appを使うようになって困ったことな今のところ無くて、ランチャーアプリで立ち上げるときに「iterm」と打ってしまうくらいでじきに慣れると思う。 困ること本当に無くて、iTerm2の機能全然使えてなかったんだなってことを改めて実感した。 しばらくこれで使ってみて、やっぱりあの機能ほしかったなと感じたらiTerm2に戾るかも。

builderscon tokyo 2019に参加して、小学生の時見学したスーパーカミオカンデのセッションを聞いて無闇に感動してしまった

これはbuilderscon tokyo 2019の感想エントリの名を借りた自身の思い出感情吐露エントリです。

builderscon tokyo 2019に参加しました。今回が初参加だったのですが、テーマの「知らなかった、を聞く」の名の通り普段業務やちょっと興味がある程度の関心では知ることのなかった領域の話を聞けてとても刺激的だった。

builderscon.io

自分は特にyhayatoさんの「スーパーカミオカンデの開発と運用」が面白いと感じて、スーパーカミオカンデで観測したデータを集計する際の気にする数値のオーダーが全然違ったり、ケーブルの長さが異なると伝送にかかる時間が変わってくるので長さを全部揃えたり、普通に使われるネットワークスイッチが自分達のユースケースにマッチするものが無くものを検討したりだとか、普段触れるものとのスケールの違いを感じさせる発表でとても面白かった。

この発表ただ面白い以上に嬉しいの感情があり、実は14年前の小学5年生だった時にスーパーカミオカンデの見学に行く機会があって、まさにあの光電子増倍管の並ぶ水タンクの上を見学した経験があった。 当時の感想は中やたら寒いなとか、この下にあのランプが並ぶ水槽があるの?へー(ポカーン)だったり、宇宙関係の実験施設でとにかく中々見学出来る機会の無い場所ですごいらしい程度の感想しか無かったけど、それから年月が経ってWebエンジニアの職に就いてビルコンのセッションで再会して、技術者目線の感想が出たり面白さを感じること出来たのが良かったしすごく嬉しかった。

当時も小学生なりに宇宙すごいみたいなスケールの大きさを感じていたと思うけれど、今は具体的に観測したデータをどう処理するのかといった話を聞いて普段触れるものとの違いを感じている。これは自分の関心の領域が変わってきたことと、それを理解できているんだなってことに気がついて、自身の年月の経過を感じて無闇に感情的になってしまった。

小・中となんとなく理科楽しいとやってきて、高校の時にコンピュータに出会い大学で情報系、現在Webエンジニアと来たけど、まさかここで小学生の時の理科体験の1つと再会するの中々感動的だなと思う。


全ての体験が良くて、10数年前に見学に行った施設の話をビルコンみたいな場で再会したことや、目線が理科好き少年から技術者になったことや、もちろんトークが素人でもわかりやすくエンジニアも楽しめるものになってて最高だったのも良くて、全部最高だった。 今年のゲストスピーカーは僕にとってすごく嬉しいのチョイスでした。運営の皆様本当にありがとうございます。


最初の繰り返しになるけど builderscon 初めて参加して、普段触れることが無さそうな話に触れられるのめっちゃいいなと思っていて、こういうカンファレンスにどんどん参加したいと思ったし、機会があれば自分も話してみたいなと思った。

運営の皆様本当にありがとうございました!!!