コピーしてコメントアウトするVS Codeプラグインを作った

Duplicate, Then Comment-out

f:id:yskoht:20200403210439p:plain

DuplicateThenCommentOut - Visual Studio Marketplace

コピーしてコメントアウト?

f:id:yskoht:20200403204021g:plain

プログラミングをしていると既存のコードを保持した上で、少し変更して動作を確認したいことがよくあります。そういう場合、自分は対象箇所をコピーしてコメントアウトするという操作を行います。

従来は、

  1. 範囲選択
  2. コピー&ペースト
  3. 範囲選択
  4. コメントアウト

と1回ずつ範囲選択して、コピペとコメントアウトをする必要がありました。 このプラグインを使うと、

  1. 範囲選択
  2. プラグイン実行

の2ステップで同じことを実現できます。

上のgifは範囲選択をしたあと、コマンドパレットを呼び出してから(Cmd + Shift + P)、Duplicate then Comment-outを選択してプラグインを実行している様子です。

ショートカットの設定

ショートカットを設定しておけば、いちいちコマンドパレットを呼び出す必要がなくなります。keybindings.jsonに以下のように追記し、key を好きなようにカスタマイズすれば、好きなショートカットでプラグインを実行できるようになります。

  {
    "key": "ctrl+shift+/",
    "command": "extension.duplicateThenCommentOut"
  },

ソースコード

github.com

Enjoy!

Binary Indexed Tree

AtCoder Beginner Contest 153

atcoder.jp

BIT知らなかった。

BIT

github.com

参考

F - Silver Fox vs Monster

class BIT
  def initialize(n)
    @tree = Array.new(n + 1, 0)
  end

  def add(i, diff)
    while i < @tree.size
      @tree[i] += diff
      i += (i & -i)
    end
  end

  def sum(i)
    t = 0
    while i > 0 do
      t += @tree[i]
      i -= (i & -i)
    end
    return t
  end
end

n, d, a = gets.chomp.split.map(&:to_i)

ms = []
n.times do
  ms.push gets.chomp.split.map(&:to_i)
end
ms = ms.sort_by { |x| x[0] }
ms.push [1<<60, 0]

dst = {}
n.times do |i|
  x, _ = *ms[i]
  r = x + 2*d
  k = ms.bsearch_index { |m| m[0] > r }
  dst[i] = k
end

bit = BIT.new(n+1)

res = 0
n.times do |i|
  x, h = *ms[i]
  t = bit.sum(i + 1)
  h -= t
  next if h <= 0

  cnt = (h + a - 1) / a
  res += cnt
  damage = cnt * a

  bit.add(i + 1, damage)
  bit.add(dst[i] + 1, -damage)
end

puts res

参考

My Technology Roadmap for DIY Keyboard (2018 ~ 2019)

新しいキーボードを作る際に技術的なチャレンジを毎回決めているのですが、 キーボードを作り始めてから2年経ったのでできるようになったことをまとめておきます。

9key (2018-01-13)

  • キーマトリクス回路の実装
  • QMK firmwareのビルド&フラッシュ
  • ICSP (in-circuit serial programming)を用いたプログラムの書き込み

yskoht.hatenablog.com

Thumbhelix (2018-05-01)

  • KiCadを用いたPCBの作成
    • シンボルライブラリの作成
    • フットプリントライブラリの作成
    • Eeschemaで回路図を作り、Pcbnewで配置配線
    • ALL PCBでPCB発注
  • アクリルでケースの作成
    • アクリルケースの図面作成
    • Ponokoで発注
    • coromozaでレーザー加工
  • I2Cライブラリの使用
  • Tokyo Mechanical Keyboard Meetup Vol.4に展示

yskoht.hatenablog.com

pakbd Prototype (2018-08-31)

  • 分割キーボードの作成
  • Tokyo Mechanical Keyboard Meetup Vol.5に展示

yskoht.hatenablog.com

pakbd Rev1 (2018-11-03)

  • PCBに切れ目を入れて5行版と4行版に対応
  • 4行レイアウトの実装
  • Cherry MX用スイッチソケットを用いたスイッチのホットスワップ
  • きりいたドットコムでステンレスプレートの発注
  • TRSケーブルの自作
  • QMK firmwareの整備

yskoht.hatenablog.com

pakbd Rev2 (2019-02-24)

  • 6行レイアウトの実装
  • スタビライザーの使用

pakbd Rev3 (2019-03-30)

  • 薄型化
  • アクリル積層ケースの作成
  • ProMicro上面実装

Business Card Keyboard (2019-09-29)

  • USB-C化
  • SK6812MINI(LED)対応PCBの作成
  • Kailh Choc用スイッチソケットを用いたスイッチのホットスワップ
  • ALL PCBでステンシルの発注
  • 自宅ホットプレートリフロー
  • 脱ProMicro (ATmega32U4の基板実装)
  • PCBのシルクにイラストの挿入

yskoht.hatenablog.com

棚卸し

8月のキーボードでやりたいことを列挙していたので棚卸しする。57%達成。

  • 基板のリファクタリング(ProMicro、スイッチ位置の修正。シルク入れ等) →✅
  • QMKファームウェアの整備 →✅
  • 薄型化 →✅
  • LED対応 →✅
  • ホットスワップソケット対応 →✅
  • ミドルプレートの作成 →✅
  • ステンレスプレートの作成 →✅
  • tilt機構 →❌
    • 興味がなくなったのでキャンセル
  • 左右一体で使う場合のロック機構→❌
    • あまり持ち運ばないのでキャンセル
  • トラックポイント→❌
    • そろそろDIY Keyboardにおいての回答がほしい。継続
  • USB-C化 →✅
  • 静電容量無接点スイッチ対応→❌
    • 勉強が足りない。継続
  • ARM化、脱QMK→❌
    • 勉強が足りない。継続
  • Bluethooth化 →❓ yskoht.hatenablog.com

今後のやりたいこと

来年はファームウェア周りをやっていきたい

  • ファームウェアの自作
  • ARM化
  • ポインティングデバイス
  • 静電容量無接点スイッチ対応

番外編 (作ったツールなど)

Keymapviz (2018-03-01)

QMK Firmwareのkeymap.cからアスキーアートを出力するスクリプト。PyPlに登録したんですが、いまいまJSONを出力するのが壊れていますね。すみません。正月休み中に直します🙇(あと記事が古くなっているのも直したい。直接でPNGが出力できるようにもしたい)→直しました(2019-01-05)

yskoht.hatenablog.com

Keyboard Layouter Plugin (2019-04-01)

KiCadでスイッチを自動配置するプラグイン。

yskoht.hatenablog.com

HardTyping (2019-12-25)

タイピングサイト。がんばっていきたいです。よろしくお願いします🙇

yskoht.hatenablog.com

自作キーボード向けタイピングサイトを作った

この記事はキーボード #2 Advent Calendar 2019の25日目の記事です。昨日は😢🔰神🔰😢さんのトラックボールマウスを分解してArduino Microで動かした話でした。かなりヌルヌル動いていてすごい。

adventar.org

自作キーボード向けタイピングサイト

自作キーボードを作ったいいけれど、キーマップに馴染むまでが大変で、いい感じに練習できるツールがあったらいいなと思って作りました。

hardtyping.io

特徴① レイアウトの変更が可能

レイアウトをKeyboard Layout Editor形式のJSONで自由に変更できます。JSONの詳細はレイアウトの設定を御覧ください。

f:id:yskoht:20191224212914g:plain

特徴② レイヤーに対応

数字や記号はRAIZEレイヤーやLOWERレイヤーに割り当てられることが多いですが、既存のタイピングアプリではなかなか練習しづらいです。HardTypingでは押下するキーとレイヤーを変更するためのもモディファイアキーも一緒にハイライトしてくれます。

f:id:yskoht:20191224220411g:plain

今後の展望

現在ベータ版です。やりたいことは色々あるんですがまだ全然できていません。

  • タイピングする文章をユーザーが投稿できるようにしたい
  • キーマップをユーザーがシェアできるようにしたい
    • keymap.cからレイアウト設定用のJSONを出力したり、JSONからkeymap.cを出力したりしたい
  • ランキング機能をつけたい
  • コンテンツを増やしたい
  • かな入力に対応したい
  • キーボード上のホットスポットを可視化したい
  • 統計情報を出力したい

新しい配列を気軽に試したり、練習できたり、配列の試行錯誤に便利なツールになれば良いなと思います。ゆっくりやっていこうと思うので今後ともよろしくお願いします🙇‍♂️

この記事はpakbd rev3を使って書かれました。

名刺サイズキーボードを作った

Business Card Keyboard

meishi展1に行ってからずっと作りたいなと思っていた名刺サイズ(91mm×55mm)のキーボードを作りました。

f:id:yskoht:20190929135905p:plain

Feature

ATmega32U4の基板実装

f:id:yskoht:20190929135912p:plain

今回やりたかったことのメイントピックは脱ProMicroでした。回路図は公開されているので問題なかったですが、部品は何を使えばよいのかよくわからない感じです。今回は「適当に集める→動いた」で良かったですが、HomeReflowKit – スイッチサイエンスを改めてよく見たら部品表が載っていたので、これを集めればよかったのかもしれません(というか、このHomeReflowKitが欲しい)。個別に部品を買うと50個、100個単位でしか買えなかったりするので、部品が大量に余ってしまいました。

USB-C化

f:id:yskoht:20190929140025p:plain

やりたかったことの2つ目はUSB-C化でした。使った部品は以下です。

このパーツの素晴らしいところはピッチ変換基板があり、しかもそのEagleファイルがGithubに公開されている2ところです。

EagleファイルをKiCadに変換するだけでフットプリントの用意ができました。そして、「これは手ハンダは無理だ」となり、自宅リフローを決意しました。

ALL PCBでステンシルの発注

PCBとステンシルをALL PCBで発注しました。ステンシルが$20.00、PCBが$5.00、送料は$27.00で合計$52.00でした。「ステンシル高ぇ、送料高ぇ」と思っていたら届いたものが思いの外大きく、そりゃこれくらいしますね、という感じでした。

自宅ホットプレートリフロー

f:id:yskoht:20190929182433p:plain
ハンダ盛りすぎ

f:id:yskoht:20190929182445p:plain
チップマウンター(手)

f:id:yskoht:20190929182449p:plain
「このホットプレートじゃ60%キーボードの基板とか入らないじゃん」と後になって思いました

f:id:yskoht:20190929182458p:plain
「早く冷まさなくては」とビビっていたんですが、ATmega32U4がハンダ不良起こしていたり、基板の隅のパーツがついていなかったりしたのでもっとじっくりリフローするべきでした。

f:id:yskoht:20190929182503p:plain
手ハンダとはまた違う出来栄え

SK6812MINI (LED) 対応PCBの作成

f:id:yskoht:20190929185607j:plain

せっかくなのでLEDも付けました。フットプリントはHelixを参考にさせていただきました。シンボルはKiCadに入っていたものを使ったため、シンボルとフットプリントの端子番号が整合せず、間違って配線してしまったので、PCBを再発注しました。横着せずにシンボルも自分で作ったほうが安全ですね。最初に発注した基板はSK6812MINIがぴったりハマっていい感じだったのですが、再発注した基板は若干緩い感じで製造誤差を実感しました。

Kailh Choc用スイッチソケットを用いたスイッチのホットスワップ

f:id:yskoht:20190929191152p:plain

今回はKailhのChocスイッチを使いたかったのでCPG135001S30を使いました。フットプリント作るのめんどくさいなと思っていたところ、foostan/kbd: Publish data for Keyboardで公開されていることを知りました。速攻でスター付けて利用させていただきました。ありがとうございます。

PCBのシルクにイラストの挿入

f:id:yskoht:20190929191358p:plain

線とビアは重ならないようにしたほうが良さそうです。

失敗したこと

スレーブ側でhas_usbが1になってしまった

今回、参考にした回路図3にはVBUSのラインにダイオードが付いていませんでした。

f:id:yskoht:20190929233156p:plain

なので、マスター側のVCCをTRRSケーブルを通じてスレーブ側のVCCにつなげると、スレーブ側のUVCCもHIGHになり、スレーブ側でもhas_usbが1になってしまいました。

Pro micro スイッチサイエンス版の回路図にはしっかりダイオードが入っているので、こちらを参考にするのが良さそうだなと思います。

[Rails] Active Record Transactions の requires_new

transactionをネストしたときに、子のtransaction内でActiveRecord::Rollbackを投げてもロールバックされない。

irb(main):059:0> User.transaction do
irb(main):060:1*   User.create(username: 'Kotori')
irb(main):061:1>   User.transaction do
irb(main):062:2*     User.create(username: 'Nemu')
irb(main):063:2>     raise ActiveRecord::Rollback
irb(main):064:2>   end
irb(main):065:1> end
   (0.1ms)  begin transaction
  User Create (0.7ms)  INSERT INTO "users" ("username", "created_at", "updated_at") VALUES (?, ?, ?)  [["username", "Kotori"], ["created_at", "2019-07-18 14:31:50.104487"], ["updated_at", "2019-07-18 14:31:50.104487"]]
  User Create (0.1ms)  INSERT INTO "users" ("username", "created_at", "updated_at") VALUES (?, ?, ?)  [["username", "Nemu"], ["created_at", "2019-07-18 14:31:50.106418"], ["updated_at", "2019-07-18 14:31:50.106418"]]
   (1.7ms)  commit transaction
=> nil

irb(main):066:0> User.all.pluck(:username)
   (0.1ms)  SELECT "users"."username" FROM "users"
=> ["Kotori", "Nemu"]

requires_new: trueとすると子のtransaction内だけロールバックする。

irb(main):073:0> User.transaction do
irb(main):074:1*   User.create(username: 'Kotori')
irb(main):075:1>   User.transaction(requires_new: true) do
irb(main):076:2*     User.create(username: 'Nemu')
irb(main):077:2>     raise ActiveRecord::Rollback
irb(main):078:2>   end
irb(main):079:1> end
   (0.1ms)  begin transaction
  User Create (0.3ms)  INSERT INTO "users" ("username", "created_at", "updated_at") VALUES (?, ?, ?)  [["username", "Kotori"], ["created_at", "2019-07-18 14:33:44.891135"], ["updated_at", "2019-07-18 14:33:44.891135"]]
   (0.0ms)  SAVEPOINT active_record_1
  User Create (0.1ms)  INSERT INTO "users" ("username", "created_at", "updated_at") VALUES (?, ?, ?)  [["username", "Nemu"], ["created_at", "2019-07-18 14:33:44.892205"], ["updated_at", "2019-07-18 14:33:44.892205"]]
   (0.0ms)  ROLLBACK TO SAVEPOINT active_record_1
   (1.5ms)  commit transaction
=> nil

irb(main):080:0> User.all.pluck(:username)
   (0.1ms)  SELECT "users"."username" FROM "users"
=> ["Kotori"]

ちなみに、普通に例外を投げると親のtransactionもロールバックする。普通。

irb(main):087:0> User.transaction do
irb(main):088:1*   User.create(username: 'Kotori')
irb(main):089:1>   User.transaction do
irb(main):090:2*     User.create(username: 'Nemu')
irb(main):091:2>     raise
irb(main):092:2>   end
irb(main):093:1> end
   (0.1ms)  begin transaction
  User Create (0.8ms)  INSERT INTO "users" ("username", "created_at", "updated_at") VALUES (?, ?, ?)  [["username", "Kotori"], ["created_at", "2019-07-18 14:36:22.828138"], ["updated_at", "2019-07-18 14:36:22.828138"]]
  User Create (0.2ms)  INSERT INTO "users" ("username", "created_at", "updated_at") VALUES (?, ?, ?)  [["username", "Nemu"], ["created_at", "2019-07-18 14:36:22.830153"], ["updated_at", "2019-07-18 14:36:22.830153"]]
   (1.4ms)  rollback transaction
Traceback (most recent call last):
        3: from (irb):87
        2: from (irb):89:in `block in irb_binding'
        1: from (irb):91:in `block (2 levels) in irb_binding'
RuntimeError ()

irb(main):094:0> User.all.pluck(:username)
   (0.1ms)  SELECT "users"."username" FROM "users"
=> []

参考

[Ruby] 再帰的group_by

やりたいこと

こういうデータ↓を

[
  { prefecture: '埼玉県', city: '深谷市',        town: '北根',   address: '3-5' },
  { prefecture: '埼玉県', city: '深谷市',        town: '北根',   address: '3-7-19' },
  { prefecture: '埼玉県', city: '深谷市',        town: '堀米',   address: '1-11-14' },
  { prefecture: '埼玉県', city: 'さいたま市中央区', town: '新中里', address: '1-2-17' },
  { prefecture: '埼玉県', city: 'さいたま市中央区', town: '新都心', address: '1-6-13' },
  { prefecture: '東京都', city: '品川区',        town: '西中延', address: '3-5' },
]

こう↓したい

{
  "埼玉県"=> {
    "深谷市"=> {
      "北根"=>["3-5", "3-7-19"],
      "堀米"=>["1-11-14"]
    },
    "さいたま市中央区"=> {
      "新中里"=>["1-2-17"],
      "新都心"=>["1-6-13"]
    }
  },
  "東京都"=> {
    "品川区"=> {
      "西中延"=>["3-5"]
    }
  }
}

再帰的group_by

参考:

module Enumerable
  def recursive_group_by(*props, &block)
    groups = group_by{ |e| e[props.first] }
    next_props = props.drop(1)

    if next_props.empty?
      block_given? ? block.call(groups) : groups
    else
      groups.transform_values do |e|
        e.recursive_group_by(*next_props, &block)
      end
    end
  end
end

data = [
  { prefecture: '埼玉県', city: '深谷市',           town: '北根',   address: '3-5' },
  { prefecture: '埼玉県', city: '深谷市',           town: '北根',   address: '3-7-19' },
  { prefecture: '埼玉県', city: '深谷市',           town: '堀米',   address: '1-11-14' },
  { prefecture: '埼玉県', city: 'さいたま市中央区', town: '新中里', address: '1-2-17' },
  { prefecture: '埼玉県', city: 'さいたま市中央区', town: '新都心', address: '1-6-13' },
  { prefecture: '東京都', city: '品川区',           town: '西中延', address: '3-5' },
]

new_data = data.recursive_group_by(:prefecture, :city, :town) do |groups|
  groups.transform_values do |v|
    v.map { |x| x[:address] }
  end
end

pp data
puts '--'
pp new_data

結果

[{:prefecture=>"埼玉県", :city=>"深谷市", :town=>"北根", :address=>"3-5"},
 {:prefecture=>"埼玉県", :city=>"深谷市", :town=>"北根", :address=>"3-7-19"},
 {:prefecture=>"埼玉県", :city=>"深谷市", :town=>"堀米", :address=>"1-11-14"},
 {:prefecture=>"埼玉県", :city=>"さいたま市中央区", :town=>"新中里", :address=>"1-2-17"},
 {:prefecture=>"埼玉県", :city=>"さいたま市中央区", :town=>"新都心", :address=>"1-6-13"},
 {:prefecture=>"東京都", :city=>"品川区", :town=>"西中延", :address=>"3-5"}]
--
{"埼玉県"=>
  {"深谷市"=>{"北根"=>["3-5", "3-7-19"], "堀米"=>["1-11-14"]},
   "さいたま市中央区"=>{"新中里"=>["1-2-17"], "新都心"=>["1-6-13"]}},
 "東京都"=>{"品川区"=>{"西中延"=>["3-5"]}}}