次期家PC

DELLのnew XPS13 2 in 1に少し期待していたが、盆休み前に表参道のDELL Design Laboに実機を見に行ったところ、MagLevキーボードが思ったより打鍵感がペコペコで期待外れだった。持ち運びを考えなければSurface Bookが良さそうだが、今年は新機種が出るのかなあ。

追記(2019/09/24)

ビックカメラで実機を触ってみたら、以外とこのキーボードで行ける気がした。 増税前の割引セールをやってるのでどうしようかな。 10月に発表と噂のSurfaceの新型も気になるが。

追記(2019/09/26)

再度、店舗で実機をじっくりと確かめてみた。 キーボードはそんなに悪くないが、今度はディスプレイのペラさが気になった。 Surfaceの方がディスプレイの作りがかっちりしていて視認性もよさそう。 少なくともDELLは今回は見送りだな。

ivy/counselの設定

いろいろ試行錯誤して次の設定に落ち着いた。

ivy

基本設定

(require 'ivy)
(ivy-mode 1)
(setq ivy-height 30)
(setq ivy-use-virtual-buffers t)

helmのように正規表現を順不同にする

(setq ivy-re-builders-alist '((t . ivy--regex-ignore-order)))

デフォルトで入力済みの^を消す

鬱陶しいのでデフォルト値から消した。

(setq ivy-initial-inputs-alist
      (seq-remove
       (lambda (initial-input-pair)
         (let ((initial-input-name (car initial-input-pair)))
           (or (string-prefix-p "counsel-" (symbol-name initial-input-name))
               (seq-contains '(Man-completion-table woman) initial-input-name))))
       ivy-initial-inputs-alist))

検索候補を語長順かつ辞書順にする

Emacs-Lispのコマンド・関数・変数の検索が対象。

(defun ivy--sort-by-len (name candidates)
  "Sort CANDIDATES based on similarity of their length with NAME."
  (let ((name-len (length name))
        (candidates-count (length candidates)))
    (if (< 500 candidates-count)
        candidates
      (seq-sort-by (lambda (candidate-string)
                     (cons candidate-string
                           (abs (- name-len (length candidate-string)))))
                   (lambda (a b)
                     (or (< (cdr a) (cdr b))
                         (and (= (cdr a) (cdr b))
                              (string< (car a) (car b)))))
                   candidates))))
(dolist (i '(counsel-M-x
             counsel-apropos
             counsel-describe-function
             counsel-describe-variable
             counsel-describe-face))
  (setf (alist-get i ivy-sort-matches-functions-alist) 'ivy--sort-by-len))

swiper

呼び出すだけ。

(require 'swiper)

counsel

基本設定

(require 'counsel)
(counsel-mode 1)
(global-set-key (kbd "M-x") 'counsel-M-x)                  ; replace to counsel command
(global-set-key (kbd "C-x C-b") 'ivy-switch-buffer)        ; replace to counsel command
(global-set-key (kbd "ESC M-x") 'execute-extended-command) ; backup original command

counsel-gitにUntracked Filesを追加

helm-ls-gitに動作を近づける。

(setq counsel-git-cmd
      (string-join '("git status --short --untracked-files=all | awk '$1~/?/{print $2}'" ; additional untracked files
                     "git ls-files --full-name --")                                      ; default git command
                   "; "))

counsel-gitのエイリアス

counsel-git-grepと被るので、区別がつくようにエイリアスを定義。

(defalias 'counsel-git-ls 'counsel-git)

counsel-git-grepに別ウィンドウのアクションを追加

標準だとなぜか未定義なので追加した。

(defun counsel-git-grep-other-window (&rest args)
  "Go to occurrence X in current Git repository other window.
ARGS is passed through to `counsel-git-grep-action'."
  (switch-to-buffer-other-window (current-buffer))
  (apply #'counsel-git-grep-action args))
(ivy-add-actions
 'counsel-git-grep
 '(("j" counsel-git-grep-other-window "other window")))

magitの設定

(with-eval-after-load "magit"
  (setq magit-completing-read-function 'ivy-completing-read))

prescient

基本設定

(require 'prescient)
(setq prescient-aggressive-file-save t)
(prescient-persist-mode 1)
(require 'ivy-prescient)
(ivy-prescient-mode 1)

検索候補を辞書順に並べる

デフォルトだと同じ語長のワードがランダムに並んで 気持ち悪いので、adviceを使って無理やり辞書順に並べ替えた。

(defun my-advice/ivy-prescient-sort-function-by-dictionary-order (adviced-sort-function c1 c2)
  "Around advice function for `ivy-prescient-sort-function'.
This advice sorts candidates by dictionary order.
ADVICED-SORT-FUNCTION is original function.
C1 and C2 is original arguments."
  (or (funcall adviced-sort-function c1 c2)
      (progn
        (when (listp c1)
          (setq c1 (car c1)))
        (when (listp c2)
          (setq c2 (car c2)))
        (and (= (length c1) (length c2))
             (string< c1 c2)))))
(advice-add 'ivy-prescient-sort-function :around #'my-advice/ivy-prescient-sort-function-by-dictionary-order)

7月に読んだ本やマンガ

7月の読書メーター
読んだ本の数:34
読んだページ数:6928
ナイス数:1

プラネット・ウィズ 3 (3巻) (ヤングキングコミックス)プラネット・ウィズ 3 (3巻) (ヤングキングコミックス)
読了日:07月31日 著者:水上 悟志
データ指向アプリケーションデザイン ―信頼性、拡張性、保守性の高い分散システム設計の原理データ指向アプリケーションデザイン ―信頼性、拡張性、保守性の高い分散システム設計の原理
読了日:07月30日 著者:Martin Kleppmann
天竺熱風録 6 (ヤングアニマルコミックス)天竺熱風録 6 (ヤングアニマルコミックス)
読了日:07月30日 著者:伊藤勢
はぐれアイドル地獄変 (9) (ニチブンコミックス)はぐれアイドル地獄変 (9) (ニチブンコミックス)
読了日:07月30日 著者:高遠 るい
変女~変な女子高生 甘栗千子~ 13 (ヤングアニマルコミックス)変女~変な女子高生 甘栗千子~ 13 (ヤングアニマルコミックス)
読了日:07月30日 著者:此ノ木よしる
暴走するネット広告: 1兆8000億円市場の落とし穴 (NHK出版新書)暴走するネット広告: 1兆8000億円市場の落とし穴 (NHK出版新書)
読了日:07月23日 著者:NHK取材班
ヒストリエ(11) (アフタヌーンKC)ヒストリエ(11) (アフタヌーンKC)
読了日:07月23日 著者:岩明 均
超人ロック ドラゴンズブラッド (3) (MFコミックス フラッパーシリーズ)超人ロック ドラゴンズブラッド (3) (MFコミックス フラッパーシリーズ)
読了日:07月22日 著者:聖 悠紀
超人ロック ドラゴンズブラッド (2) (MFコミックス フラッパーシリーズ)超人ロック ドラゴンズブラッド (2) (MFコミックス フラッパーシリーズ)
読了日:07月22日 著者:聖 悠紀
超人ロック ドラゴンズブラッド 1 (MFコミックス フラッパーシリーズ)超人ロック ドラゴンズブラッド 1 (MFコミックス フラッパーシリーズ)
読了日:07月22日 著者:聖 悠紀
中国文明の歴史 (講談社現代新書)中国文明の歴史 (講談社現代新書)
読了日:07月21日 著者:岡田 英弘
ダーウィンの覗き穴〔マンガ版〕──虫たちの性生活がすごいんですダーウィンの覗き穴〔マンガ版〕──虫たちの性生活がすごいんです
読了日:07月19日 著者:日高 トモキチ
ライドンキング(2) (シリウスKC)ライドンキング(2) (シリウスKC)
読了日:07月19日 著者:馬場 康誌
ライドンキング(1) (シリウスKC)ライドンキング(1) (シリウスKC)
読了日:07月19日 著者:馬場 康誌
超人ロック 荒野の騎士 (MFコミックス)超人ロック 荒野の騎士 (MFコミックス)
読了日:07月15日 著者:聖 悠紀
超人ロック ひとりぼっちのプリンセス超人ロック ひとりぼっちのプリンセス
読了日:07月14日 著者:聖 悠紀
超人ロック 星辰の門 (MFコミックス)超人ロック 星辰の門 (MFコミックス)
読了日:07月14日 著者:聖 悠紀
超人ロック カデット (MFコミックス)超人ロック カデット (MFコミックス)
読了日:07月14日 著者:聖 悠紀
なぜ日本人はご先祖様に祈るのか ドイツ人禅僧が見たフシギな死生観 (幻冬舎新書)なぜ日本人はご先祖様に祈るのか ドイツ人禅僧が見たフシギな死生観 (幻冬舎新書)
読了日:07月14日 著者:ネルケ 無方
オイラー、リーマン、ラマヌジャン―時空を超えた数学者の接点 (岩波科学ライブラリー)オイラー、リーマン、ラマヌジャン―時空を超えた数学者の接点 (岩波科学ライブラリー)
読了日:07月12日 著者:黒川 信重
超人ロック メヌエット (MFコミックス)超人ロック メヌエット (MFコミックス)
読了日:07月12日 著者:聖 悠紀
超人ロック 猫の散歩引き受けます (MFコミックス)超人ロック 猫の散歩引き受けます (MFコミックス)
読了日:07月12日 著者:聖 悠紀
超人ロック 天空の魔法士 (MFコミックス)超人ロック 天空の魔法士 (MFコミックス)
読了日:07月11日 著者:聖 悠紀
超人ロック クランベールの月 (MFコミックス)超人ロック クランベールの月 (MFコミックス)
読了日:07月11日 著者:聖 悠紀
超人ロック ブレインシュリンカー/不死者たち (MFコミックス)超人ロック ブレインシュリンカー/不死者たち (MFコミックス)
読了日:07月11日 著者:聖 悠紀
「宇宙のすべてを支配する数式」をパパに習ってみた 天才物理学者・浪速阪教授の70分講義 (KS科学一般書)「宇宙のすべてを支配する数式」をパパに習ってみた 天才物理学者・浪速阪教授の70分講義 (KS科学一般書)
読了日:07月09日 著者:橋本 幸士
異種族レビュアーズ 3 (ドラゴンコミックスエイジ ま 7-1-3)異種族レビュアーズ 3 (ドラゴンコミックスエイジ ま 7-1-3)
読了日:07月09日 著者:masha
ウチの使い魔がすみません(6) (アフタヌーンKC)ウチの使い魔がすみません(6) (アフタヌーンKC)
読了日:07月08日 著者:櫓刃 鉄火
ダーウィンズゲーム(18) (少年チャンピオン・コミックス)ダーウィンズゲーム(18) (少年チャンピオン・コミックス)
読了日:07月08日 著者:FLIPFLOPs
米中もし戦わば 戦争の地政学 (文春文庫)米中もし戦わば 戦争の地政学 (文春文庫)感想
歴史的経緯や体制の良し悪しや善悪の判断や方法論はともあれ、中国が覇権に向けて戦略的に動いているのが分かる。中国の怖さは次の一文が分かりやすい。「ペンタゴンの元アナリスト、マイケル・ピルズベリーによれば、中国は総合国力を信じられないほど正確に計算しているという。その最も注目すべき点はおそらく、軍事力が国力全体の一〇%程度にしか評価されていないことだ」
読了日:07月07日 著者:ピーター ナヴァロ
王家の遺伝子 DNAが解き明かした世界史の謎 (ブルーバックス)王家の遺伝子 DNAが解き明かした世界史の謎 (ブルーバックス)
読了日:07月05日 著者:石浦 章一
異種族レビュアーズ 2 (ドラゴンコミックスエイジ ま 7-1-2)異種族レビュアーズ 2 (ドラゴンコミックスエイジ ま 7-1-2)
読了日:07月05日 著者:masha
異種族レビュアーズ (ドラゴンコミックスエイジ ま 7-1-1)異種族レビュアーズ (ドラゴンコミックスエイジ ま 7-1-1)
読了日:07月05日 著者:masha
TCP技術入門 ――進化を続ける基本プロトコル (WEB+DB PRESS plusシリーズ)TCP技術入門 ――進化を続ける基本プロトコル (WEB+DB PRESS plusシリーズ)
読了日:07月02日 著者:安永 遼真,中山 悠,丸田 一輝

読書メーター

Emacsの補完機能をHelmからIvyへ移行

バズってたQiitaの記事を参考に、HelmからIvyに移行してみた。

qiita.com

自分が加えた変更は次のとおり。

counsel-M-xの絞り込み開始後のソーティング方法を辞書順に

長さでソートした後、さらに辞書順でソートするようにした。

(defun ivy--sort-by-len (name candidates)
  "Sort CANDIDATES based on similarity of their length with NAME."
  (let ((name-len (length name))
        (candidates-count (length candidates)))
    (if (< 500 candidates-count)
        candidates
      (seq-sort-by (lambda (candidate-string)
                     (cons candidate-string
                           (abs (- name-len (length candidate-string)))))
                   (lambda (a b)
                     (if (not (= (cdr a) (cdr b)))
                         (< (cdr a) (cdr b))
                       (string< (car a) (car b))))
                   candidates))))
(dolist (i '(counsel-M-x
             counsel-apropos
             counsel-describe-function
             counsel-describe-variable
             counsel-describe-face))
  (setf (alist-get i ivy-sort-matches-functions-alist) 'ivy--sort-by-len))

helm-ls-gitパッケージの代替コマンドを定義

helm-ls-gitに相当するcounselのパッケージが見つからなかったので、 Find file in a Git repo with ivy · (or emacsを参考に代替コマンドを定義した。

追記: あとで見直したらcounsel-gitというコマンドが定義済みだった。なんで見逃したんだろ。

"other window"アクションをどうしても追加したくて方法がわからず苦労したが、ソースコードやヘルプを色々と探したらどうやらivy-set-actionsで設定することが分かった。 ivy-readcallerは何のために設定するのかと思ったらivy-set-actionsに登録するコマンド名と対応するようだ。

;; replaced from helm-ls-git
(ivy-set-actions
 'counsel-git-ls
 '(("j" find-file-other-window "other window")
   ("f" find-file-other-frame "other frame")))
(defun counsel-git-ls (&optional initial-input)
  "Find file in the current Git repository.
When INITIAL-INPUT is non-nil, use it in the minibuffer during completion."
  (interactive)
  (let* ((default-directory (locate-dominating-file
                             default-directory ".git"))
         (candidate-git-files (split-string
                               (shell-command-to-string
                                "git ls-files --full-name --")
                               "\n")))
    (ivy-read "Git files: " candidate-git-files
              :initial-input initial-input
              :action #'find-file
              :caller 'counsel-git-ls)))

Rubyスクリプトでパスワード入力

io/consolerquireしてからIO#getpassを使う。

$ ri IO#getpass
= IO#getpass

(from ruby core)
------------------------------------------------------------------------
  io.getpass(prompt=nil)       -> string

------------------------------------------------------------------------

Reads and returns a line without echo back. Prints prompt unless it is
nil.

You must require 'io/console' to use this method.

Rubyの文字列検索いろいろ

単純な文字列検索ならString#include?が速い。 正規表現なら余分なMatchDataを生成しないRegexp#match?が速い。 MatchDataが生成されると遅くなる。 String#~は何らかの最適化が入っているようで、検索後にMatchDataにアクセスしなければRegexp#match?の次に速い。

通常はString#~を使って、速度が気になるときはRegexp#match?が使えるか検討するとよいかな。

ベンチマークスクリプト:

#!/usr/local/bin/ruby
# -*- coding: utf-8; frozen_string_literal: true -*-

require 'benchmark'

s = '12345 foo 67890'

puts 'String#include?'
p(s.include? 'foo')

puts 'Regexp#match?'
p(/foo/.match? s)

puts 'Regexp#match'
p(/foo/.match s)

puts 'String#=~'
p(s =~ /foo/)

Benchmark.bm(35) do |x|
  num_trial = 1_000_000
  x.report('empty') {
    num_trial.times do
      # nothing to do.
    end
  }
  x.report('[match] String#include?') {
    num_trial.times do
      s.include? 'foo'
    end
  }
  x.report('[match] Regexp#match?') {
    num_trial.times do
      /foo/.match? s
    end
  }
  x.report('[match] Regexp#match') {
    num_trial.times do
      /foo/.match s
    end
  }
  x.report('[match] String#=~') {
    num_trial.times do
      s =~ /foo/
    end
  }
  x.report("[match] String#=~; $`; $&; $'") {
    num_trial.times do
      s =~ /foo/; $`; $&; $'
    end
  }
  x.report('[match] String#=~; $~') {
    num_trial.times do
      s =~ /foo/; $~
    end
  }
  x.report('[no match] String#include?') {
    num_trial.times do
      s.include? 'bar'
    end
  }
  x.report('[no match] Regexp#match?') {
    num_trial.times do
      /bar/.match? s
    end
  }
  x.report('[no match] Regexp#match') {
    num_trial.times do
      /bar/.match s
    end
  }
  x.report('[no match] String#=~') {
    num_trial.times do
      s =~ /bar/
    end
  }
  x.report("[no match] String#=~; $`; $&; $'") {
    num_trial.times do
      s =~ /bar/; $`; $&; $'
    end
  }
  x.report('[no match] String#=~; $~') {
    num_trial.times do
      s =~ /bar/; $~
    end
  }
end

実行結果:

String#include?
true
Regexp#match?
true
Regexp#match
#<MatchData "foo">
String#=~
6
                                          user     system      total        real
empty                                 0.109375   0.000000   0.109375 (  0.109012)
[match] String#include?               0.250000   0.000000   0.250000 (  0.256767)
[match] Regexp#match?                 0.312500   0.000000   0.312500 (  0.301961)
[match] Regexp#match                  0.890625   0.000000   0.890625 (  0.897493)
[match] String#=~                     0.421875   0.000000   0.421875 (  0.423258)
[match] String#=~; $`; $&; $'         0.531250   0.000000   0.531250 (  0.550842)
[match] String#=~; $~                 0.921875   0.000000   0.921875 (  0.933610)
[no match] String#include?            0.187500   0.000000   0.187500 (  0.183050)
[no match] Regexp#match?              0.265625   0.000000   0.265625 (  0.279306)
[no match] Regexp#match               0.406250   0.000000   0.406250 (  0.413185)
[no match] String#=~                  0.328125   0.000000   0.328125 (  0.324284)
[no match] String#=~; $`; $&; $'      0.359375   0.000000   0.359375 (  0.357800)
[no match] String#=~; $~              0.343750   0.000000   0.343750 (  0.371355)