emahiro/b.log

Drastically Repeat Yourself !!!!

VSCode の Explorer でファイルやディレクトリを非表示にする

Overview

タイトルの通りです。
node_modules などのディレクトリがあると一度開くだけで Explorer がとても広がってしまうのが鬱陶しなくなってしまったので非表示にすることにしました。

手順

Setting.json に以下を追加するだけです。

  "files.exclude": {
      // 略
      "**/node_modules":true, // 追加
      "**/.vscode":true, // 追加
  },

僕は VSCode の設定ディレクトリもコードを書く時には特にいらないなと思ったので外しています。

A little copying is better than a little dependency について考えてみる

これはたまに書いてる自分自身の思考の dump の一つです。
思考を吐き出してるだけなので、所々話が飛んでるかもしれないですがご愛嬌で。

サマリ

Go では A little copying is better than a little dependency.  という諺があり、これは依存 < 冗長を選択した方がいいケースがあることを示している。
プログラミングのお作法としての DRY を否定しているかのような諺であるが、実際にプロダクト開発をしていくにつれてこの諺の示すところの片鱗が少し見えてきたので、実体験を元にして文章にまとめてみる。

ref: https://go-proverbs.github.io

そもそも依存が嫌われるわけ

言ってしまえば、プロダクトを実際に作っている時には、DRY の原則に基づいた方が効率がいいと思う。
同じコードを2度以上書くのは面倒くさい(※1)

ただ、DRY の原則に則ってプロダクトのコードを書いてきたにもかかわらず、ある時からこの DRY の原則に従って生み出されたものに嫌気がさすタイミングがある。 それはどういうタイミングなのか、と考えると依存が大きく、かつ依存先の処理を理解しないと影響が範囲が読めずに「コードを簡単に変更できない」という事態に遭遇した時だ。

プロダクトは長く使われる中で改善され、改変されていく。刹那的なプロダクトや個人開発で自由気ままに開発しているプロダクトであればまぁ好きに書いてくれていいが、社会に対して何らかの価値を提供し、継続的に運用されているプロダクトは、常に何らかの改善や機能の追加がなされていく。

この改善や機能追加のタイミングで既存のコードに手を加える時、あるいは自分たちが使ってるライブラリや環境が古く、ソフトウェアが危険(セキュリティなど)に晒される可能性がある時に、既存のコードを変更したりライブラリを入れ替えたりする作業が必要になる。

これはソフトウェアエンジニアとして仕事をしていれば当然直面する課題であり、都度対応していかないといけない問題でもある。

しかしながら、一度コードを変更するときにその影響範囲について必ず調査をする。 なぜなら、いくらソフトウェアが危険に晒されてるからといって、プロダクトの振る舞いを変えたり、不具合を誘発しては事業そのものに影響が出る。あくまで事業に影響が出ないように、安全にプロダクトを改善するためにも、実際自分が今から変えようとしてるコードはどこに影響が出るのか?ということは必ず事前に調査し、明らかにしておく必要がある。

ちょっとしたコードの変更やロジックの修正、もしくはコードの削除、局所的にしか使われていないライブラリや共通処理の改善であれば話は早い。そこだけ入れ替えれば作業は完了する。

しかし、ちょっとした修正をするつもりが、該当箇所の依存先の実装に問題があったとしたらどうだろう?依存先のコードは、対応したかった箇所とは別の箇所でも使われているかもしれない。依存先の依存先でまた問題のある実装が見つかるかもしれない。 もしかしたら依存先のコードの修正はプロダクト全体に影響が出るところかもしれない。

プロダクトを開発する時には DRY の原則にしたがって実装したコードが、プロダクトを運用する時になって足枷になることは実は頻繁に発生する。

コピペを嫌って脳死で共通化した処理や、DRY の原則に基づいて開発当時は有用だった依存が運用フェーズになって重荷になる、要はプロダクトの中の情報のあり方やドメインが変わってきたときに脳死で DRY したり便利だと思っていた汎用処理がプロダクトの変更の重荷になる、ということが発生する。

※1. そもそもの DRY の原則とは「単なる重複をなくすことでない」ので誤認なきよう -> Don't repeat yourself - Wikipedia

意味が違うコピペ

繰り返しになるが、そう入っても同じ処理を何度も書くのは面倒くさい。それはそうだと思う。

意味で考えるにはコードそのものよりも HTML で考えてみるとわかりやすい。
(これは前に僕の尊敬するエンジニアから教わった例えをそのまま記載している。)

<h1>見出し1</h1>
<h2>見出し2_1</h2>
<h2>見出し2_2</h2>

という HTML の構造を考えた時に h2は同じことが書かれてるな? と思って

<h1>見出し1</h1>
<h2>
    見出し2_1
    見出し2_2
</h2>

こんな風に共通化する人はいないだろう?
DRY は極端にいえばこういうことすら肯定してしまう原則とも捉えられかねない。

h1 と h2 で意味するところが違う。それどころかページのレイアウトそのものにも影響を与えてしまう。

同じ処理だから脳死で共通化するとこういうことすら肯定してしまう。
重要なのは「共通化の意味」をちゃんと考えることだ。分かれていることに意味があるならそれは「意味のある冗長」であり、共通化するべきでない。

HTMLでの例えは多少乱暴かもしれないが、プロダクトのコードを書いてるとふとこうしたことを平気でしてしまいそうなくらい、共通化の悪魔の引力は強い。

処理として同じ文脈で同じ処理を書いているなら共通化するのが原則だが、文脈が違うなら共通化はするべきではない。

依存先の継続的な更新がある場合にのみ依存する

通化処理については上記に述べたとおりであるが、広く使われているOSSや自作ライブラリに依存するケースについて考えてみる。

自分はこれについては1つの見解を持っていて、見出しにあるように「継続的な更新が提供される場合のみ汎用的なライブラリに依存するべき」と考える。 継続的な更新が提供されない限りは、自作ライブラリすら作らない方がいい、という見解で、もし作るのであれば、プロダクト開発から離れてもなお、メンテナンスされ続ける体制を作っておくくらいのことをして初めて自作ライブラリをプロダクトに適用良いのだと思う。

おそらくこれを満たすものというのは、AWSGCP謹製のライブラリや、OSSとして広く定着しているものに限られる。個人で作ってるプロジェクト等のライブラリに依存するのは極力避けておくほうが、開発時期は負担が大きいかもしれないが、長期的な運用まで見据えると旨味が大きい。

結局大事なのは捨てやすさ

まぁそうは言ってもスケールする、継続するかもわからないプロダクトの開発において大事なのはスピードで、運用を見据えて多少冗長な実装をとって時間を食ってしまうことがそもそも事業的に許容されないケースというのは絶対に存在する。
※ 自作のライブラリのメンテナンスなんか考えてる人はさらに稀かもしれない。

実際に困った時になって「依存してるのが辛いな〜」とか「冗長に書いても良かったんじゃないかな〜」と思うことがほとんどだと思う。とすれば1つの観点として持っておきたいのは、依存や共通化はしてもいいけど、それが「どれだけ捨てやすい」のか?ということではないだろうか?

影響範囲を局所的にしておく、捨てやすい(or 入れ替えやすい)ように作っておくなど、プロダクトを開発するときに頭の片隅に置いておきたい観点は存在するので、それを引き出しとして持っておくことが大事なのかなと思う。

まとめ

当初書きたかった内容からは多少逸れてしまったが、自分のキャリアで大規模なプロダクトの開発を経験する中で Go の A little copying is better than a little dependency. という諺の言わんとしてるところの片鱗を考え始めるようになった。

ほんと、プロダクト開発は奥が深くて面白い。

プライベートリポジトリを go get する

Overview

go get でプライベートリポジトリを fetch するのに、コケてその調査で時間を溶かすことが何度か重なったので対応方法について記載します。

またプライベートリポジトリに依存してるプロジェクトで Github Actions を回すときもプライベートリポジトリの取得部分でハマったのでそちらについても記載します。

プライベートリポジトリを go get する

何もしないままプライベートリポジトリを go get すると

go get -u github.com/PRIVATE_REPO
github.com/PRIVATE_REPO@vX.X.X: verifying module: github.com/PRIVATE_REPO: reading https://sum.golang.org/lookup/github.com/PRIVATE_REPO: 410 Gone
    server response: not found: github.com/PRIVATE_REPO: invalid version: unknown revision v0.0.1

というようにエラーが出て module を fetch することはできません。

これを解消するには以下の3つの設定を行います。

  • https の代わりに ssh を使用。
    • git config --add --global url."git@github.com:".insteadOf https://github.com
  • GOPROXY を direct に設定。
    • export GOPROXY=direct
  • GOPRIVATE にプライベートリポジトリを設定。
    • export GOPRIVATE=github.com/PRIVATE_REPO

Github Actions でプライベートリポジトリを go get する

Github Actions でも何もせずにプライベートリポジトリを pull しようとすると以下のエラーに遭遇してmodule を取得することができせん。

go: github.com/PRIVATE_REPO: invalid version: git fetch -f origin refs/heads/*:refs/heads/* refs/tags/*:refs/tags/* in /home/runner/go/pkg/mod/cache/vcs/3703b101c339d9fd970d99639a980444379be4a036303c73173fe24bc3ab8ac5: exit status 128:
    remote: Invalid username or password.
    fatal: Authentication failed for 'https://github.com/PRIVATE_REPO'

プライベートリポジトリを fetch するためには Actions がプライベートリポジトリへのアクセス権限を持っているが必要になります。
このため、Actions でビルドされるステップの中でビルドに使われているコンテナ内の git の設定でアクセス権を付与します。

アクセス権についてはプライベートリポジトリを見ることができるユーザーごとの Personal Access Token を取得します。 取得方法については Setting > Developer setting > Personal access token からユーザーごとのアクセストークンを払い出すことができます。この時に repo にチェックをつけて access token を生成し、それを Actions の secrets に加えます。

Actions を定義している yml には以下の設定が追加されます。

jobs:
  build:
    steps:
      - name: github private modules access
        run: git config --global url."https://${{ secrets.GO_MODULES_TOKEN }}:x-oauth-basic@github.com/".insteadOf "https://github.com/"

リポジトリの Settings > Secrets で生成した個人の Access Token を GO_MODULES_TOKEN を key にして追加します。

これで Github Actions 上でもプライベートリポジトリを fetch できるようになります。

See Also

python のバージョン起因で gcloud コマンドの実行に失敗する

Overview

タイトルの通りです。

gcloud components update したら python のバージョン違いで gcloud コマンドが動かなくなったのでその解決方法について記載します。

エラーの内容

Traceback (most recent call last):
  File "/Users/$UserName/google-cloud-sdk/lib/gcloud.py", line 104, in <module>
    main()
  File "/Users/$UserName/google-cloud-sdk/lib/gcloud.py", line 62, in main
    from googlecloudsdk.core.util import encoding
  File "/Users/$UserName/google-cloud-sdk/lib/googlecloudsdk/__init__.py", line 23, in <module>
    from googlecloudsdk.core.util import importing
  File "/Users/$UserName/google-cloud-sdk/lib/googlecloudsdk/core/util/importing.py", line 23, in <module>
    import imp
  File "/usr/local/Cellar/python@3.9/3.9.0_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/imp.py", line 23, in <module>
    from importlib import util
  File "/usr/local/Cellar/python@3.9/3.9.0_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/importlib/util.py", line 2, in <module>
    from . import abc
  File "/usr/local/Cellar/python@3.9/3.9.0_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/importlib/abc.py", line 17, in <module>
    from typing import Protocol, runtime_checkable
  File "/usr/local/Cellar/python@3.9/3.9.0_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/typing.py", line 26, in <module>
    import re as stdlib_re  # Avoid confusion with the re we export.
  File "/usr/local/Cellar/python@3.9/3.9.0_1/Frameworks/Python.framework/Versions/3.9/lib/python3.9/re.py", line 124, in <module>
    import enum
  File "/Users/$UserName/google-cloud-sdk/lib/third_party/enum/__init__.py", line 26, in <module>
    spec = importlib.util.find_spec('enum')
AttributeError: module 'importlib' has no attribute 'util'

要は python 3.9 には Google Cloud SDK は対応してねーよってことらしいです。

stackoverflow.com

対応

どうやら brew を使って python をインストールしていると意図せず裏側で最新の python が最新にあってしまっていたようで( brew upgrade した時とかの弊害) gcloud コマンドから参照する python のバージョンが最新にあってしまっていました。

これを解決するためにたまたま pyenv を入れていたいので pyenv 経由で 3.8 をインストールして合わせました。

pyenv install 3.8.0
pyenv global 3.8.0
source .zshrc
python --version
Python 3.8.0

これで再度 gcloud コマンドを実行したら正常に動作しました。

Firestore の Go SDK でドキュメントを操作する

Overview

firestore でドキュメントを操作するベースとなる方法を記載します。 ※ Go の実装で書いてます。

ref: https://godoc.org/cloud.google.com/go/firestore

Firestore の基本的な操作

取得

Get

ctx := context.Background()
client, err := firestore.NewClient(ctx, "$projectID")
if err != nil {
    // TODO: Handle error.
}
ref, err := client.Collection("$CollectionName").Doc("$DocumentID").Get(ctx)
if err != nil {
    // TODO: Handle error
}

ref: https://godoc.org/cloud.google.com/go/firestore#DocumentRef.Get

GetAll

ctx := context.Background()
client, err := firestore.NewClient(ctx, "$projectID")
if err != nil {
    // TODO: Handle error.
}
dss, err := client.Collectipn("$CollectionName").Documents(ctx).GetAll()
if err != nil {
    // TODO: Handle error
}

dsts := make([]*DistStruct, len(refs))
for i, ss := range dss {
    var dst = DistSturct{}
    if err := ss.DataTo(&dst); err != nil {
        // Handle Error
    }
    dsts[i] = &dst
}

ref:

追記

公式のドキュメントに記載されている実装方法は https://godoc.org/cloud.google.com/go/firestore#DocumentIterator を取得してからループで一つ一つマッピングしていく実装方針が記載されていますが、https://godoc.org/cloud.google.com/go/firestore#DocumentSnapshot を先に取り出したほうがその後 Slice にマッピングする時に Slice を length 指定でメモリ効率化できるので実装方針としてはそちらを採用する方がいいのでは?と思いました。

Save

ctx := context.Background()
client, err := firestore.NewClient(ctx, "$projectID")
if err != nil {
    // TODO: Handle error.
}
src := map[string]interface{}{}
ref, result, err := client.Collection("$CollectionName").Add(ctx)

ref: https://godoc.org/cloud.google.com/go/firestore#CollectionRef.Add

更新

ctx := context.Background()
client, err := firestore.NewClient(ctx, "$projectID")
if err != nil {
    // TODO: Handle error.
}
updates := []firestore.Update {
    {Path: "$updateTargetField", Value: interface{}{} }
}
if err := client.Collection("$DocumentName").Update(ctx, updates); err != nil {
    // TODO: Handle error.
}

ref: https://godoc.org/cloud.google.com/go/firestore#DocumentRef.Update

firestore.FieldPath

Update 構造体の中に FieldPath がありますが、ある Document 内部の Filed が入れ子の場合に特定の field を更新したい場合に使います。

ref: https://firebase.google.com/docs/reference/node/firebase.firestore.FieldPath

 // A FieldPath is a non-empty sequence of non-empty fields that reference a value.
 //
 // A FieldPath value should only be necessary if one of the field names contains
 // one of the runes ".˜*/[]". Most methods accept a simpler form of field path
 // as a string in which the individual fields are separated by dots.
 // For example,
 //   []string{"a", "b"}
 // is equivalent to the string form
 //   "a.b"
 // but
 //   []string{"*"}
 // has no equivalent string form.
 type FieldPath []string

ref: https://github.com/googleapis/google-cloud-go/blob/master/firestore/fieldpath.go#L31-L43

そのため以下のような構造のドキュメントを考えた時に

type Article struct {
    User User `json:"user" firestore:"user"`
}

type User struct {
    Name string `json:"name" firestore:"name"`
    Age    int64  `json:"age" firestore:"age"`
}

Article 内部のユーザーの名前を変更したい時に以下のような FieldPath を組み立てることになります。

fp := []string{"user", "name"}
update := []firestore.Update {FieldPath: fp, Value: "Taro"}
if err := client.Collection("Article").Update(ctx, update); err != nil {
    // TODO: Handle error.
}

Transaction

if err := client.RunInTransaction(ctx, func(ctx context.Context, tx *firestore.Transaction){
    // Transaction 
}); err != nil {
    // TODO: Handle error.
}

ref: https://godoc.org/cloud.google.com/go/firestore#Transaction

指定したディレクトリを goimports の対象から外す

import 順を自動で sort するときに、goimports の対象から外したいディレクトリがあるときに $GOPATH/src/.goimportsignore を設定するといいらしいけど、リポジトリごとのディレクトリ分けに依存したり、個人の開発環境に依存するかなーと思ったで、なんとかならないなかーと思ってとりあえずの対策として

gist.github.com

というのを作りました。

MySQL Driver で time.Time 型をパースする

以下のような Struct を考える。

type User struct {
    ID int64
    Name String
    CreatedAt time.Time
    UpdatedAt time.Time
}

time.Time の時刻型をもつフィールドがある時、MySQL の時刻型のカラムをそのまま Scan しようとすると以下のエラーが出る。 sql: Scan error on column index 3: unsupported Scan, storing driver.Value type []uint8 into type *time.Time

解決策としては接続する時に ?parseTime=true をつける。

https://stackoverflow.com/questions/45040319/unsupported-scan-storing-driver-value-type-uint8-into-type-time-time が参考になる。

久しぶりに sqlx を使ったら色々忘れてた

Overview

すごい久しぶりに sqlx を使ったら色々忘れてたので備忘録です。

github.com

driver は blank import しておかないといけない

driver の種類は下記を参照 https://github.com/golang/go/wiki/SQLDrivers

例えば MySQL を Driver として選択したい場合には MySQL を使うパッケージで

import _ "github.com/go-sql-driver/mysql"

とする必要があります。
これは別に sqlx に限った話じゃないですが完全に忘れてました笑

StructScan に Slice は当てられない

sqlx のメリットの1つに db.Query を使ったときにマッピングするカラムを全て指定しないといけないと言う標準の sql/database のデメリットを回避し、DB のテーブルに対応する struct を定義してき、db.StructScan を使うとマッピングを自動でやってくれると言うのがあると思いますが、この db.StructScan には Struct 以外を当てることはできません。

Slice で取り出したいときは以下のようにします。

type User struct {
    id ing64 `db:"id"`
    name string `db:"name"`
    age int64 `db:"name"`
}

rows, err := db.Queryx("SELECT * FROM user WHERE age > 20")

users := make([]*User, 0, 0)

for rows.Next() {
    user := User{}
    if err := db.StructScan(&user); err != nil {
        // handle error
    }
    users = append(users, &user)
}

追記: README 見たら db.Select が使える。ORM っぽく使いたいならアリかも。

フィールドを指定しないと missing destination name エラーが発生する

取り出したいカラムは指定しろってことですね。 つまり以下のようなクエリは発行しても struct にマッピングできません。

テーブル: User
- id INT
- name VARCHAR
- age INT
type User struct {
    ID int64 `db:"id"`
    Name string `db:"name"`
}
 
rows, err := db.Queryx("SELECT * FROM user where id = ?", 1)
if err != nil {
    // error handling
}
user := User{}
for rows.Next() {
    if err := rows.SturctScan(&user); err != nil {
        // missing destination name age が発生する 
    }
}

取り出したいカラムを制限したい場合はクエリで取り出すフィールドを指定する必要があります。

type User struct {
    ID int64 `db:"id"`
    Name string `db:"name"`
}
  
rows, err := db.Queryx("SELECT id, name FROM user where id = ?", 1)
if err != nil {
    // error handling
}
user := User{}
for rows.Next() {
    if err := rows.SturctScan(&user); err != nil {
      // missing destination name age が発生する 
    }
}

ghq を使い始める

Overview

特に避けていたわけではないですが、これ!っていうメリットが特に感じていなかったので ghq を使っていなかったのですが、ふと思い立って使い始めたのでその記録です。

Install ghq

GIthub の README に書いてある通りです。

$ go get github.com/x-motemen/ghq

brew でもインストールできます。

ghq.root ディレクトリの設定

ghq での探索対象を設定します。
ghq の設定には ~/.gitconfig の設定を使います。
自分は普段 ~/src/github.com というディレクトリ構成で個人のPC環境を作っているので、 .gitconfig に以下の設定を追加しました。

[ghq]
root = ~/src/github.com

これで ghq list を実行した時に local の ~/src/github.com を探索対象にしてくれます。
ちなみにdefault では ~/ghq になるみたいです。

alias の設定

エイリアス を設定して、一発で cd $(ghq root)/$(ghq list | peco) を使えるように .zshrc に以下を追加しました。

alias g='cd $(ghq root)/$(ghq list | peco)'

peco とセットにして使うことでインクリメンタルサーチを使えてかつ、その指定したディレクトリに移動までできるようにしています。

まとめ

調べればわかることばかりですが、ghq をとりあえず探索のみで使っているけどシンプルでいい感じです。
ディレクトリ移動は楽になりました。

See Also

VSCode で GitHub と連携する

vscode-githubの画像
vscode-github

Overview

local 環境で VSCode で作業することがほぼベースになってきたので、VSCodeGitHub を連携させて VSCode 上からできることを増やすために設定を追加したのでその記録を記載します。

GitHub for VSCode

VSCode は少し前から VSCode単体で GitHub と連携できるようになっています。 https://vscode.github.com/Add GitHub for Visual Studio Code を選択すれば連携できます。 VSCode からも左下のアカウントマークを押下して、連携シーケンスに移行できます。

vscode.github.com

連携すると設定周りが Sync されるようです。今まで VSCode での設定の共有といえば Setting Sync でしたが今後は VSCode 備え付けの GitHub ログインで良さそうです。
設定の同期機能については以下に詳しく記載されています。

code.visualstudio.com

code.visualstudio.com

GitHub Pull Requests and Issues

GitHub 上の PullReuqest や Issue を VSCode 上から操作できるようになります。

marketplace.visualstudio.com

コードレビューするときはローカルでチェックアウトして実際のコードをみながらレビューする、ということを手間に思うことがあって(それもどうなんだ、、、というツッコミはあると思います。)提出されたファイル差分のみでレビューすることが多かったのですが、VSCode 上でチェックアウトできますし、そのまま VSCode 上で補完や Code Jump も使えるので、これでどれくらいレビューが楽になるのか、その辺を使いながら見てみようと思います。

こういったツールは今までもいくつか出てましたが、private リポジトリも含めて連携しちゃうので権限周りを考えるとちょっと及び腰でした。ただ、このツール自体は GitHub 純正のものなので、全面信頼するわけではないですが、ある程度信頼性があると踏んで使い始めてみてます。

概要は以下に記載してあります。

code.visualstudio.com

VSCode から Issue を作成する

GUI から Issue を作成します。

テンプレートが作成されますので、Issue に記載したい内容を載せて右上のチェックマークを押下します。

Issue が作成されます。Open Issue で実際の Issue に遷移できます。

実際に作成された Issue は以下です。

github.com

VSCode から Pull Request を作成する

まずコードを変更します。 今回はテストなので、自分の学習ログの Readme を書き換えます。

また事前以下のことをやっておきます。

  • ブランチを作る
  • 差分をコミットしておく

実際に VSCode から Pull Request を作成します。

ベースとなるブランチを選択します。デフォルトは master に対する Pull Request になります。

Pull Request のタイトルを選択します。

大体書いてある通りですが

  • commit: ブランチを push して Web GUI から Pull Request を作成する場合のデフォルトのタイトル設定です。よくある、トピックブランチのファーストコミットメッセージがそのまま Pull Request のタイトルになる設定です。
  • branch: ブランチ名が Pull Request のタイトルになります。
  • custom: 自分で Pull Request のタイトルを決めます。今回は custom を使います。

Pull Request のタイトル名を入力して Enter を押します。そしたら Pull Request が作成されます。

実際にPull Request が以下のように作成されます。

github.com

GitHub Theme

GitHub 純正の VSCode の Theme。 見やすい気がするので使ってみてます。

marketplace.visualstudio.com

まとめ

GitHubマイクロソフトの傘下になってからというもの VSCode 関連のインテグレートの進化が凄すぎて、いろいろついていけてませんでしたが、VSCodeGitHub をベッタベタに密結合させると VSCode が優秀な Github クライアントになるので、今後もいろいろ試してみようと思います。

VSCode の Column Select Mode を Off にする

サマリ

タイトルの通りです。 いつからか、VSCode でマウスを使って範囲選択を行うときに選択された範囲が Column Select Mode の状態になってしまってました。 意図してそうしていたならともかく意図せずに On になっていたので VSCode が急に使いづらくなってました。

Column Select Mode

こういうやつ。

便利だけど、マウスカーソルでの選択にも影響出るとは知らずにびっくりしました。

Off の仕方

メニューバーにある Selection から Column Select Mode を Off にします。 Cmd + Shift + * でも On/Off を変更することができます。

Local での DynamoDB の動作確認方法

サマリ

DynamoDB を使った機能を Local で動作確認したいケースでは以下の2つのツールが使えそう。

  1. DynamoDB Local
  2. ddbcli

DynamoDB Local

ブラウザでGUIとして操作できる。

Docker image があるので落としてくればそのまま使える。

hub.docker.com

ddbcli

ターミナル SQL ライクに使える

github.com

qiita.com

進捗を無にする方法

あれこれコードを書いてて、結局その差分が全然意味をなさなくて、自分の差分を無に返したい時がありますね。 僕はあります。

そんな時は

$ git checkout .

// or

$ git restore .

を使います。

これで自分の進捗を無にすることできます。 間違っても実装途中で使わないでくださいね。

VSCode で Go の自動補完に gopls + TabNine を使ってみる

Summary

qiita.com

上記のエントリを読んで試しに、VSCode + TabNine の組み合わせをセットアップしたら gopls を使っているにも関わらず、Go の自動補完が爆速になって開発者体験がめちゃくちゃ向上したのでその手順を記載しています。
と言うのも、元々 Intelij を使っていたのですが、gopls の開発活発化に合わせて、ここ1年くらいは VSCode + gopls の環境で作業をしていました。ただ、どうしても VSCode と gopls をセットで使う場合に、langage server との通信ラグがコードの補完と微妙にあってなく、僕が思ってる速度でコードが書けないと言う課題がありました。
そんな中使い続けてたら Go を暗記し始めたので、実質補完いらないみたいな世界線に最近片足突っ込み始めてますが笑

まぁめちゃくちゃPCのメモリを食うのでまぁファンが鳴り止まない、と言う自体もあって何かいいツールはないものか(ここで Intelijにまた戻るのか?)探し続けてたところ、TabNine を知りました。

TabNine とは?

www.tabnine.com

tkrel.com

僕はこんな機会があるまで全然知らなかったんですが、 深層学習を用いることで、より精度の高い入力補完を実現 するツールのようです。
(すごい世の中ですね。)

VSCode のセットアップ

gopls

Goを使う場合は基本的には gopls がついてくるので、特に何かをインストールする必要はありません。
最新、と言うか master の gopls を使いたい場合は go get -u golang.org/x/tools/gopls で取得します。

VSCodesetting.json は以下のようにします。

"format": false,              
"autoComplete": true,         
"rename": true,               
"goToDefinition": true,       
"hover": true,                
"signatureHelp": true,        
"goToTypeDefinition": true,   
"documentSymbols": true,      
"workspaceSymbols": true,     
"findReferences": true,       
"diagnostics": true,          
"documentLink": true,         
"goToImplementation": true,   

余談ですが、最近 gofmt を off った状態で使用しています。どうやら VSCode 側の format on save (go.formatTool で指定したツール) とバッティングせずに動作が少し改善?されるような気がしてます。
詳しいところは追っかけてませんが、gofmt を true にしていたときは大きなファイルに変更を加えると VSCode の fmt のプロセスがなかなか終わらずファイルが save されない、と言う状況を起こしてしまっていました。 gofmt を off って状態でも VSCode 側での goimports に任せるようにした場合、特に fmt や import で困ることもなかったので、結果としては保存時のストレスは最近の作業に限って言うとなくなりました。

TabNine

Setup

参考にしたエントリに記載されているように TabNine のプラグインをインストールします。
プラグインを入れたらソースコードのどこかに TabNine::config を打ちます。そうするとデフォルトブラウザ TabNine の設定画面が開きます。 ブラウザが開かれるとわかりますが、 http://127.0.0.1:5555/{$任意の文字列} と言うURLになっています。このURLはユーザーごとに違うのかまではわかりません。僕の環境では払い出されるURLは毎回同じでした。

エントリには設定方法が記載されていますが、202007 時点では v2.8 系が出ており、これは特に設定等もいらずに TabNine::config で設定を開いたと同時にモデルのDLが始まっていました。

Local or Cloud

TabNine には Local モードと Cloud モードがあります。Cloud モードはクラウド上にある TabNine のサーバー上でコードの自動補完を検査するようです。これは意図せずコードが外部に流出してしまう可能性があるので業務レベルで使いたい場合は Local 一択でしょう。

ちなみにクラウドの機能をフルで使うには有償版に乗り換えないといけないっぽいです。セットアップ時はメールアドレスを入力して送信するだけでしたが。。。(ベータ版はもう終わったのだろうか。)

www.tabnine.com

設定画面に activation するための key を入力するところがありましたし、Cloud 版を試してみたい方は有償版に切り替えて使ってみて欲しいです。

気になる点

深層学習による、コードの補完サポート、と言う立ち位置なので、独自のパッケージ構成や変数定義については完全には追従してません。特にローカル版においては。
頻出パターンなどを含めて学習したモデルを使って自動補完の候補を出力しているので、プライベートなリポジトリの結果が反映されないのも当たり前といえば当たり前ですが。。。 ただ、多少のシグネチャの揺れは合ってもある程度実用に耐えそうな感覚はあります。これから使い倒していくうちに見方が変わるかもしれません笑

まとめ

思いの外開発者体験が向上したので興味がある方は是非1度使ってみてください。
正直まだ使い始めたばかりなので、しんどいところを経験してないですが、もし何かあればまたアップデートをかけようと思います。

VSCodeでMySQLを使用するためのプラグイン

Overview

VSCodeMySQL を使うときのプラグインについて記載します。

主に達成したいことは以下です。

  • クエリの自動補完

インストールしたプラグイン

以下の2つのうちどちらかを入れておけば問題なさそうです。

marketplace.visualstudio.com

marketplace.visualstudio.com

注意点はくれぐれも *.sql ファイルで発火する SQLServer はインストールしないことです。これは Azure 向けのエクステンションであるので MySQLシンタックスが使えないことがあります。
ずっとおかしいなぁなんでエラー出るんだろうと思ってましたが、後から description 読んでそりゃそうだろ...みたいな気持ちになりました。

追記

20220820

フォーマットをかけて欲しくない + 簡単な MySQL の補完だけしてほしい、という要件であればいいので、以下の Extention が特に変なことをしない、という点で良さそうでした。

marketplace.visualstudio.com

20220906

もうこれでいいんじゃないか説があります(上記の全ての extention がフォーマットかかってしまう)

marketplace.visualstudio.com