defx.nvim でカーソル移動をするたびに preview ウィンドウを開く
defx.nvim で auto-preview 的なことをやってみた。
augroup my-defx autocmd! autocmd FileType defx call s:defx_my_settings() augroup END function! s:defx_my_settings() abort augroup ftplugin-my-denite autocmd! * <buffer> autocmd CursorMoved <buffer> call defx#call_action("preview", []) augroup END endfunction
単に CursorMoved
時に preview ウィンドウを開くようにしているだけですね。
実際にやってみるとそんなに重くないのでありっちゃありな気がする。
2021/04/08 今週の気になった bugs.ruby のチケット
内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。
[PR irb #219] Add pry-like show_source command
irb
に pry のようなshow_source
コマンドが実装された- これは以下のようにメソッドなどの定義を表示してくれるコマンド
$ RBENV_VERSION=3.1.0-dev irb irb(main):001:0> require "erb" => true irb(main):002:0> show_source "ERB#result" From: /home/worker/.rbenv/versions/3.1.0-dev/lib/ruby/3.1.0/erb.rb:901 def result(b=new_toplevel) unless @_init.equal?(self.class.singleton_class) raise ArgumentError, "not initialized" end eval(@src, b, (@filename || '(erb)'), @lineno) end => nil irb(main):003:0> show_source "ERB.new('<%= hello %>').src" From: /home/worker/.rbenv/versions/3.1.0-dev/lib/ruby/3.1.0/erb.rb:843 attr_reader :src => nil irb(main):004:0> show_source "IRB::VERSION" From: /home/worker/.rbenv/versions/3.1.0-dev/lib/ruby/3.1.0/irb/version.rb:14 VERSION = "1.3.5" => nil irb(main):005:0>
- どんどん irb がよくなっていって便利
[PR irb #202] process multi-line pastes as a single entity
class A def b; self; end def c; true; end end a = A.new a .b # aaa .c (a) &.b() class A def b; self; end; def c; true; end; end; a = A.new a .b # aaa .c (a) &.b()
$ irb irb(main):001:1* class A irb(main):002:1* def b; self; end irb(main):003:1* def c; true; end irb(main):004:0> end => :c irb(main):005:0> irb(main):006:0> a = A.new => #<A:0x00007fe44496c078> irb(main):007:0> irb(main):008:0> a irb(main):009:0> .b irb(main):010:0> # aaa irb(main):011:0> .c => true irb(main):012:0> irb(main):013:0> (a) irb(main):014:0> &.b() => #<A:0x00007fe44496c078> irb(main):015:0> irb(main):016:0> irb(main):017:0> class A def b; self; end; def c; true; end; end; => :c irb(main):018:0> a = A.new => #<A:0x00007fe444c7c880> irb(main):019:0> a irb(main):020:0> .b irb(main):021:0> # aaa irb(main):022:0> .c => true irb(main):023:0> (a) irb(main):024:0> &.b() => #<A:0x00007fe444c7c880> irb(main):025:0>
- こんな感じで評価できるようになって初めて irb 上でコードが実行されて次のコードがペーストされる
- 途中のコードは途中で実行される点には注意する
[Feature #17773] Alias Numeric#zero?
and Float#zero?
as Numeric#empty?
and Float#empty?
Numeric#zero?
とFloat#zero?
をNumeric#empty?
とFloat#empty?
のエイリアスにしようという提案- モチベーションとしては Web などの入力フィールドで 文字列などと同じように
0
を無効値として扱うために共通のメソッドとして#empty?
がほしいという感じ 0
自体は集合ではないし、0
が意図的に入力された可能性もあるなど全体的には否定的なコメントが目立つ- ちなみに
ActiveSupport
の#blank?
は0.blank? == false
になる - 共有のメソッド名にするならもっと別の名前の方がよい気はするな〜
[Bug #17777] 2.6.7 fails to build on macOS: implicit declaration of function 'rb_native_mutex_destroy' is invalid in C99
2021/04/01 今週の気になった bugs.ruby のチケット
内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。
[Bug #17757] Hash#slice does not keep compare_by_identity on the results
Hash#compare_by_identity
を使用するとレシーバの Hash をキーの一致判定をオブジェクトの同一性で判定するようにするObject#object_id
で判定するようにする
hash = { "a" => 1, b: 2 } # デフォルトでは無効 p hash.compare_by_identity? # => false # OK p hash["a"] # => 1 p hash[:b] # => 1 # Object#object_id で判定するようにする hash.compare_by_identity p hash.compare_by_identity? # => true # ハッシュのキーの "a" と 参照するときの "a" の object_id は異なるので参照できない p hash["a"] # => nil # Symbol は同じ object_id なので参照できる p hash[:b] # => 2
- この
compare_by_identity
だが特定のメソッドを経由して生成した Hash でcompare_by_identity
の性質を受け継いだりしなかったりするというバグ報告
hash = { "a" => 1, b: 2 } # Object#object_id で判定するようにする hash.compare_by_identity # こっちは compare_by_identity を保持するが p hash.except("a").compare_by_identity? # => true # こっちは compare_by_identity を保持しない p hash.slice("a").compare_by_identity? # => false
- なるほど…?
compare_by_identity
なんてものがあるの知らなかった
[Bug #17754] NoMethodError#to_s makes segmentation fault when Module#name returns non string value
- 以下のように
.name
が文字列以外を返した場合に SEGV するというバグ報告
class C def self.name 42 # これなら OK # "42" end end # C に対して NoMethodError なエラーが発生すると SEGV する C.this_method_does_not_exist
- これは Ruby 3.0 で再現して 2.7 や 2.6 では再現しなかった
- 3.1.0-dev で既に修正済み
- よく見つけるなあこんなバグ…
[Bug #17756] StringScanner#charpos makes segmentation fault when target.byteslice returns non string value
- 以下のように
StringScanner
を使用すると SEGV するというバグ報告byteslice
を上書きしてnil
を返すと落ちるっぽい?
require 'strscan' string = 'ruby' scnanner = StringScanner.new(string) pre = Module.new do def byteslice(*args) end end string.singleton_class.prepend(pre) scnanner.charpos
- これは既に対応済み
- そもそも
StringScanner
の存在を知らなかった- 指定した文字列をスキャンするためのライブラリ
- Ruby | StringScanner で字句解析 - Qiita
- Rubyの標準ライブラリStringScannerがすごい! - hythofの日記
[Feature #17753] Add Module#outer_scope
- 自分が定義されている1つ上のスコープのオブジェクトを取得する
Module#outer_scope
メソッドの提案
module A module B class C; end class D; end end end # C が定義されている箇所のスコープを返す p A::B::C.outer_scope # => B # ネストして呼び出したりとか p A::B::C.outer_scope.outer_scope # => A
- ユースケースとしては次のように同じスコープの定数を列挙したい場合に利用できる
A::B::C.outer_scope.constants # => [A::B::C, A::B::D] ObjectSpace.each_object(Class) do |k| p siblings: k.outer_scope.constants end
- 現在は『
outer_scope
って名前じゃなくてnamespace
のほうがいいんじゃない?』と言われてModule#namespace
って名前になっている- https://bugs.ruby-lang.org/issues/17753#note-2
- 流石に
Module#namespace
って名前をここで使っちゃうのはなんかもったいない気がするなあ…
[Feature #17749] Const source location without name
Module#const_source_location
を『レシーバ』に対して行う機能を追加する提案A::B.const_source_location
はA.const_source_location(:B)
と同じ意味になる- PR: https://github.com/ruby/ruby/pull/4324
- Module#const_source_location (Ruby 3.0.0 リファレンスマニュアル)
module A class B end end p A::B.const_source_location # => ["test.rb", 2] # これと同じ意味 p A.const_source_location(:B) # => ["test.rb", 2] # ユースケース # const_source_location に渡す定数名がわからない場合に利用できる ObjectSpace.each_object(Class) do |k| p k.const_source_location end
- これは普通に前からほしいなーと思ってたのでうれしい
- この機能自体は『定数』とは関係ないので
const_source_location
じゃなくてsource_location
という名前に変わっている
[Feature #17768] Proposal: Downward assignments
- 次のように変数を下の行で定義する提案
# var = 42 と同じ 42 ^^var # 中間の式も代入できる p(2 * 3 * 7) #=> 42 ^^^^^var p var #=> 6
- 実際のユースケースいろいろ
while (line = gets) != nil p line end # ↓↓↓ while gets != nil ^^^^line p line end
ary = [1, 2, 3, 4, 5] ary.each {|elem| found = elem if elem.even? } p found #=> 4 # ↓↓↓ ary = [1, 2, 3, 4, 5] ary.each {|elem| elem if elem.even? } ^^^^found p found #=> 4
class C def initialize(foo, bar) @foo = foo @bar = bar end end # ↓↓↓ class C def initialize(foo, bar) ^^^@foo ^^^@bar end
- めっちゃ便利そうじゃん…(※もちろんこれはエイプリルフールネタです
- ちゃんと patch ファイルも付いてて以下のコードは普通に動いてる
(2 * 3 * 7) ^^^^^var p var while gets != nil ^^^^line p line end ary = [1, 2, 3, 4, 5] ary.each {|elem| elem if elem.even? } ^^^^found p found #=> 4
- どうやってやってるの…
[Feature #17769] Proposal: numeric coefficient syntax
2x
を2 * x
と解釈するようにする提案
irb(main):001:0> x = 3 => 3 irb(main):002:0> 2x => 6 irb(main):003:0> def pi = Math::PI => :pi irb(main):004:0> 2pi => 6.283185307179586
2x
だけではなくて2(x + 1)
という風にもかけるらしい- これもエイプリルフールネタ
- patch 内容が比較的シンプルで面白い
マージされたチケット
defx で絞り込みしつつ dentie-file/rec を起動する
defx で絞り込みしつつ dentie-file/rec を起動する
defx で find っぽいことをしたかったので設定した。
単に :Denite file/rec -input=ファイル名
を起動しているだけ。
:Defx -auto-cd
で起動しておく必要があるので注意。
" NOTE: auto-cd を有効にしておく必要があるので注意 call defx#custom#option('_', { 'auto_cd': 1, }) function! s:start_find() let find_word = input("Find Pattern: ") call denite#start([{ "name": "file/rec", "args": []}], { "input": find_word }) endfunction autocmd FileType defx call s:defx_my_settings() function! s:defx_my_settings() abort " 起動するキーマップ nnoremap <silent><buffer> gd :<C-u>call <SID>start_find()<CR> endfunction
denite.nvim の denite-menu を使って簡単にファイルを開いたりする
dentie-menu を使うことで簡単に自分好みのショートカットを設定する source を定義することができます。
" denite-menu の設定 let s:menus = {} " Denite menu:dotfile をすると開くファイルを登録する let s:menus.dotfile = { \ 'description': 'Edit your dotfile' \ } let s:menus.dotfile.file_candidates = [ \ ['vimrc', '~/.vimrc'], \ ['gvimrc', '~/.gvimrc'], \ ['bashrc', '~/.bashrc'], \ ['bash_aliases', '~/.bash_aliases'], \ ] " Denite menu:my_denites をすると開くファイルを登録する " コマンドを呼ぶ let s:menus.my_denites = { \ 'description': 'Denite list' \ } let s:menus.my_denites.command_candidates = [ \ ['file/old', 'Denite file/old'], \ ['menu:dotfile', 'Denite menu:dotofile'], \ ['grep', 'Denite grep'], \] " 登録 call denite#custom#var('menu', 'menus', s:menus) " 呼び出し nnoremap <Space>ll :Denite menu<CR>
べんりべんり。
2021/03/25 今週の気になった bugs.ruby のチケット
内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。
[PR irb #212] Complete require and require_relative
- irb で
require
のファイル名補完をする機能の追加
irb(main):001:0" require "irb/<Tab> # <- Tab を押すとファイル名を補完してくれる
- これは普通に便利そう
[PR irb #204] Add whereami command
irb
にpry
のwhereami
コマンドを追加する PRwhereami
するとbinding.irb
した付近のコードを確認することができる
$ ruby /tmp/a.rb From: /tmp/a.rb @ line 3 : 1: a = 1 2: @b = 2 => 3: binding.irb irb(main)[01:0]> a => 1 irb(main)[02:0]> @b => 2 irb(main)[03:0]> whereami From: /tmp/a.rb @ line 3 : 1: a = 1 2: @b = 2 => 3: binding.irb => nil
- pry の
whereami
コマンド知らなかった - これはめっちゃ便利そう…
[PR irb #203] Implement pry-like ls command
irb
にpry
のls
コマンドを追加する PRls
すると引数オブジェクトのメソッド一覧やインスタンス一覧を出力してくれる
$ irb irb(main):001:0> require "erb" => true irb(main):002:0> ls ERB.new('test') ERB#methods: def_class def_method def_module encoding filename filename= lineno lineno= location= make_compiler result result_with_hash run set_eoutvar src instance variables: @_init @encoding @filename @frozen_string @lineno @src => nil
- これも知らなかった
- めっちゃ便利そう…
[Feature #17411] Allow expressions in pattern matching
- 以下のように
^
を使ってパターンマッチに式をかけるようにする提案
user = { name: "homu", age: 14 } case user # 式を書く場合は ^() を使う in { age: ^(7 + 7) } end
- これが実装されてマージされた :tada:
- 早くパターンマッチ使いたい…
[Bug #17738] Ruby can still freeze ENV
ENV
は普通はfreeze
できないががんばればfreeze
できるという報告
# これは error ENV.freeze # こうすると freeze できる Kernel.instance_method(:freeze).bind(ENV).call() p ENV.frozen? #=> true
- Ruby むずかしい…
[Bug #17735] Hash#transform_keys!
drops non evaluated keys
- 以下のように
Hash#transform_valies!
のブロック内でraise
するとレシーバは変わらない
# transform_values! はレシーバの要素を保持する hash = {a: 1, b: 2, c: 3} a = hash.transform_values! { raise } rescue p hash #=> {:a=>1, :b=>2, :c=>3}
- しかし
Hash#transform_keys!
の場合はレシーバが空になる
# transform_keys! はレシーバの要素が空になる hash = {a: 1, b: 2, c: 3} hash.transform_keys!(){ raise } rescue p hash #=> {}
[Bug #17736] Destructive methods inconsistently handle receiver frozen state
in given block
Array#select!
のブロック内でレシーバをfreeze
するとレシーバが空の配列になる
array = [1, 2, 3, 42] array.select! do array.freeze false end p array #=> []
Array#uniq!
の場合は例外になりレシーバはそのままになる
array = [1, 2, 3, 42, 2, 3] begin array.uniq! do |item| array.freeze item end rescue => err p err #=> #<FrozenError: can't modify frozen Array: [1, 2, 3, 42, 2, 3]> end p array #=> [1, 2, 3, 42, 2, 3]
Array#select!
の方はバグということで修正された
array = [1, 2, 3, 42] # Ruby 3.1.0-dev だとエラー # error: `select!': can't modify frozen Array: [1, 2, 3, 42] (FrozenError) array.select! do array.freeze false end
[Bug #17739] Array#sort! changes the order even if the receiver raises FrozenError in given block
Array#sort!
のブロック内でレシーバをfreeze
すると例外が発生するがソート済みになっているというバグ報告- [Bug #17736] と同じようなバグ
array = [1, 2, 3, 4, 5] begin array.sort! do |a, b| array.freeze if a == 3 1 end rescue => err # 例外が発生する p err #=> #<FrozenError: can't modify frozen Array: [5, 4, 3, 2, 1]> end # 例外が発生してもソート済みになっている p array #=> [5, 4, 3, 2, 1]
- ちなみに
break
した場合はそこまでのソートになっている
array = [1, 2, 3, 4, 5] array.sort! do |a, b| break if a == 3 1 end # 途中までソートされた状態 p array #=> [3, 4, 2, 1, 5]
- これはまだ未修正
[Feature #17743] Show argument types in backtrace
- バックトレースに引数情報も追加する提案
- 例えば以下のようなコードを実行すると
def say_hi(person) puts message(person) end def message(person) "hi: #{person.name}" end say_hi(nil)
- 以下のようなバックトレースが出力される
/tmp/vTaZxJg/70:6:in `message': undefined method `name' for nil:NilClass (NoMethodError) from hi.rb:2:in `say_hi' from hi.rb:9:in `<main>'
- これを以下のようにする提案
hi.rb:6:in `message': undefined method `name' for nil:NilClass (NoMethodError) from hi.rb:2:in `say_hi' called with NilClass from hi.rb:9:in `<main>' called with NilClass
- どのような表記にするのか(クラスだけ?値は必要がない?複数の引数の場合は?)やパフォーマンス的な懸念点がないか議論されている
[Feature #17744] Accumulate Enumerable#tally
results
- 以下のように
Enumerable#tally
に引数を渡してそれに結果を集計していくような提案- 元々は今月の開発者会議で提案されていたっぽい
h = {} [:a,:b,:c].tally(h) [:a,:b,:d].tally(h) # 2個の tally の結果が h に蓄積される p h #=> {:a=>2, :b=>2, :c=>1, :d=>1}
- 便利な気がするけど引数が変更されてしまうのはなんかちょっと気持ち悪い…
2021/03/18 今週の気になった bugs.ruby のチケット
内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。
[PR irb #202] process multi-line pastes as a single entity
- 現行の
irb
だと以下のようなコードをペーストするとエラーになる
class A def a; self; end def b; true; end end a = A.new a .a .b
irb(main):001:1* class A irb(main):002:1* def a; self; end irb(main):003:1* def b; true; end irb(main):004:0> end = > :b irb(main):005:0> irb(main):006:0> a = A.new = > #<A:0x00005588503197b8> irb(main):007:0> irb(main):008:0> a = > #<A:0x00005588503197b8> irb(main):009:0> .a /home/worker/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.4/lib/irb/workspace.rb:116:in `eval': (irb):9: syntax error, unexpected '.' (SyntaxError) from /home/worker/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.4/exe/irb:11:in `<top (required)>' from /home/worker/.rbenv/versions/3.0.0/bin/irb:23:in `load' from /home/worker/.rbenv/versions/3.0.0/bin/irb:23:in `<main>' irb(main):010:0> .b
- これはペーストしたタイミングで Ruby のコードが逐次的に評価され
.a
を呼び出した時にエラーになってしまっているため- 先に
a
のコードが評価され
- 先に
- これを回避するためにペーストして『最後に』コードを評価するようにする PR
- 個人的には落とし所としてはいいとは思う
[Bug #17571] prependしたArray#[] が反映されない
- 以下のように
Array
にprepend
してる#[]
が呼び出されないことがある
module TestMod def [](*) :called end end Array.prepend TestMod # これは Array#[] が呼ばれる p [1, 2, 3][1] # => 2 # これは TestMod#[] が呼ばれる p [1, 2, 3][] # => :called # Method オブジェクトは TestMod を指している p [1, 2, 3].method(:[]) # => #<Method: Array(TestMod)#[](*) /tmp/vud3mdg/27:2>
- これはおそらく
Array#[]
を事前にメソッドキャッシュしておりそちらを優先して呼び出しているのが原因ぽい?- なので
prepend
しているメソッドは呼ばれなくなっている - 引数がない場合は
Array#[]
とシグネチャが異なるのでキャッシュされたメソッドではなくてprepend
されたメソッドを呼び出しているので意図する挙動になっているみたい?
- なので
- これは Ruby 2.7 では問題なくて Ruby 3.0 から問題になっている
- Ruby 3.0 に上げるタイミングでなにか壊れるかもしれないので注意したい
- 原因っぽいコミット(と PR)
[Bug #17725] Prepend breaks ability to override optimized methods
- 以下のように
String.prepend
するとString#+
が上書きされたりされなかったりする
# これは上書きされる class String def + other 'blah blah' end end p 'a' + 'b' # => "blah blah"
# これは上書きされない String.prepend(Module.new) class String def + other 'blah blah' end end p 'a' + 'b' # => "ab"
[Bug #16996] Hash should avoid doing unnecessary rehash
Hash#dup
よりもHash#merge
の方が高速だというチケット- 3.5倍早いと書かれている
- これは
Hash#dup
は内部で再ハッシュを行っているがHash#merge
は行っていないのでその差になる - Ruby なにもわからない
Hash#replace/dup/initialize_copy
で再ハッシュを行わないようにする PR- 開発者会議でも承認されたっぽいのでマージされそう
- ちなみに
hash.select
よりもhash.merge.select!
のほうが高速らしいHash#select/reject
で再ハッシュしないようにする PR- https://github.com/ruby/ruby/pull/4273
[Bug #17719] Irregular evaluation order in hash literals
Hash
リテラルでキーが重複している場合に以下のような評価順になる
# 1個目と2個目の foo の要素が先に評価される $ ruby -e '{foo:p(1), bar:p(2), foo:p(3)}' -e:1: warning: key :foo is duplicated and overwritten on line 1 1 3 2
- これはバグのようなので修正するチケット
- 以下のような問題もあるしもうエラーにしちゃっていいんじゃないかなあ…。