- 投稿日:2020-02-09T23:24:11+09:00
Pythonのbisect_leftとbisect_rightをGo言語でも使いたい
Python3には
bisect
というモジュールがあり、この中にはbisect_left
、bisect_right
という関数が用意されています。bisect_left
はソート済みの配列を二分探索により探索し、挿入点のうちもっとも左側のものを戻り値とします。bisect_right
はその逆で、挿入点のうち、もっとも右側が戻り値となります。import bisect digits = [111, 222, 333, 333, 333, 444, 555] bisect.bisect_left(digits, 333) #=> 2 bisect.bisect_right(digits, 333) #=> 5Go言語で
bisect_left
やbisect_right
と同じようなことをしたい場合、すなわち、二分探索でもっとも右側の挿入点を探したい、もっとも左側の挿入点を探したい場合、sort
パッケージのSearch
を利用し、それぞれ次のように実装します。digits := []int{111, 222, 333, 333, 333, 444, 555} sort.Search(len(digits), func(i int) bool { return digits[i] >= 333 }) //=> 2 sort.Search(len(digits), func(i int) bool { return digits[i] > 333 }) //=> 5つまり
bisect_left
の場合は不等号で比較、bisect_right
の場合は等号付き不等号で比較すればよいわけです。
- 投稿日:2020-02-09T22:04:34+09:00
Goでシグナルを監視する君を書いた
tl;dt
https://github.com/nozo-moto/signal_kansitai
目的
ちょっとした調査、アプリケーションにどんなシグナルが送られてくるか知りたかった
方法
Go言語は以下のようにすると作成したgoroutineにシグナルがあるとハンドリングできます。
c := make(chan os.Signal) signal.Notify(c) go func() { for { s := <-c log.Println("signal :", s) } } }()よしなにロギング
標準出力とログファイルに書き出したかったのでio.MultiWriter
を利用logfile, err := os.OpenFile("./signal.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666) if err != nil { panic("cannnot create log :" + err.Error()) } defer logfile.Close() log.SetOutput(io.MultiWriter(logfile, os.Stdout)) log.SetFlags(log.Ldate | log.Ltime)こんな感じでシグナルをハンドリングして流してくれる
% tail -f signal.log 2020/02/09 21:48:01 alive : 2020/02/09 21:49:01 alive : 2020/02/09 21:50:01 alive : 2020/02/09 21:50:33 signal : hangup falseSIGTERMが送られてきたら落とすようにしました。60秒に一回生存確認をしています。
また、SIGKILLとかのハンドリングできない系はハンドリングできずに死にます。知見
SIGWINCHというターミナルのWindowサイズの変更のシグナルがあることを知った。
- 投稿日:2020-02-09T19:46:47+09:00
GolangをFastCGIとしてApacheと共に動かす
tl;dt
Go言語の
net/http/fcgi
を使ってGoをCGIとしてApacheで動かしました。動かす
git clone https://github.com/nozo-moto/golang-fastcgi-try cd golang-fastcgi-try make docker-build dokcer-compose up
確認
$ curl localhost:8888 {"message":"pong"}net/http/fcgi
Go言語にはnet/http/fcgiというFastCGI protocolが実装されたパッケージがあります。
これは以下のように
http.Handler
をラップしてあげて使えます。今回はginを使っています。package main import ( "net" "net/http/fcgi" "github.com/gin-gonic/gin" ) func main() { var err error r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) l, err := net.Listen("tcp", ":8080") if err != nil { panic(err) } if err := fcgi.Serve(l, r); err != nil { panic(err) } }動かす
以下のようなdocker-composeを用意します
以下ではてきとうなコンテナに上のGoのコードのバイナリを入れて実行しています。version: '3.3' services: www: image: httpd:2.4.41 ports: - "8888:80" depends_on: - fcgi volumes: - ./docker/apache/conf/fcgi.conf:/usr/local/apache2/conf/extra/fcgi.conf command: bash -c "echo 'Include conf/extra/fcgi.conf' >> /usr/local/apache2/conf/httpd.conf && httpd-foreground" fcgi: image: nozomi0966/fastcgi-golang-tryfcgi.conf
ApacheでfastCGIを実行するための設定ファイルを書きます
上記のdocker-compose.ymlで./docker/apache/conf/fcgi.conf
に書いたのを読んでいます。
Apache2.4以降からmod_proxy_fcgiというモジュールが入っているのでそれを使います。
docker-composeでfcgi
というホスト名で引けるように動かしているので以下のようにfcgi:8080
でアクセスします。LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so SetHandler "proxy:fcgi://fcgi:8080/"以下を実行してApacheとFastCGIを動かします。
docker-compose up以下のようなレスポンスが返ってくれば動いてるのが確認できます。
$ curl localhost:8888 {"message":"pong"}
以上
以下のリポジトリにコードを載せています
https://github.com/nozo-moto/golang-fastcgi-try
- 投稿日:2020-02-09T17:17:36+09:00
AtCoder 過去問精選10問 をGo言語で解いてみた
はじめに
何番煎じかわかりませんが、Go言語の基本文法を練習するのによさそうだったので、AtCoder に登録したら次にやること ~ これだけ解けば十分闘える!過去問精選 10 問 ~を解いてみました。
普段はPythonしか書いていないので、書き方の違いにも軽く触れてみました。実際に解いてみる場合は、AtCoder Beginners Selectionに問題がまとめられているので便利です。
問題
0. PracticeA - Welcome to AtCoder
標準入出力の練習です。
標準出力は特に問題ないですが、標準入力については、先に変数を宣言してから、ポインタを使って値を更新するような形になります。ひとまずは、
fmt.Scan()
とfmt.Println()
が使えれば問題ないと思います。
fmt.Scan()
は標準入力をスペースだけでなく改行でも区切るので注意しましょう。解答例
package main import "fmt" func main() { var ( a, b, c int s string ) fmt.Scan(&a, &b, &c, &s) fmt.Println(a+b+c, s) }1. ABC086A - Product
条件分岐(if文)の練習です。
また、$a \times b$ が偶数になるのは、$a, b$ のいずれかが偶数のときです。解答例
package main import "fmt" func main() { var a, b int fmt.Scan(&a, &b) if a%2 == 0 || b%2 == 0 { fmt.Println("Even") } else { fmt.Println("Odd") } }2. ABC081A - Placing Marbles
各桁は0か1しか取らないので、それぞれが1と等しいか比較すればよいです。
比較回数が3回で固定のため、for文を使わなくても十分いけます。ただし、Go言語では文字列から
string[index]
の形で1文字だけ抜き出すと文字列ではなくバイトになるため、stringの"1"
ではなくruneの'1'
でないと正しく比較できないことに注意してください。解答例
package main import ( "fmt" ) func main() { var s string fmt.Scan(&s) ans := 0 if s[0] == '1' { ans++ } if s[1] == '1' { ans++ } if s[2] == '1' { ans++ } fmt.Println(ans) }別解
以下のように
strconv.Atoi()
を使用して数値型に変換する方法もあります。数字が0から9までの値を取る場合などは、条件式を10個書くのは面倒なのでこちらの方がよいでしょう。
ここで、strconv.Atoi()
は2つ目の戻り値としてエラーを返しますが、今回は使わないので_
で受けておきましょう。また、GoではPythonとは違って
int()
で文字列を整数に変換することはできないので注意しましょう。package main import ( "fmt" "strconv" ) func main() { var s string fmt.Scan(&s) ans := 0 tmp, _ := strconv.Atoi(string(s[0])) ans += tmp tmp, _ = strconv.Atoi(string(s[1])) ans += tmp tmp, _ = strconv.Atoi(string(s[2])) ans += tmp fmt.Println(ans) }3. ABC081B - Shift only
2で割り切れない数字が出てくるまで繰り返し割ります。繰り返し回数がわからないのでfor文の出番です。
Goにはwhile文やfor each文がありませんが、for文で代用することが可能です。また、ここでは数値の配列を管理する必要があるので、スライスを使用してみます。
スライスはmake()
を使って生成します。解答例
package main import ( "fmt" ) func main() { var n int fmt.Scan(&n) nums := make([]int, n) // Pythonの for i in range(n): に相当 for i := 0; i < n; i++ { fmt.Scan(&nums[i]) } var ans int ok := true // Pythonの while ok: に相当 for ok { // Pythonの for i, num in enumerate(nums): に相当 for i, num := range nums { if num%2 != 0 { ok = false break } nums[i] /= 2 } if ok { ans++ } } fmt.Println(ans) }4. ABC087B - Coins
多重ループの問題です。
for文を3つネストさせれば簡単に解ける問題ですが、1つ減らして計算量を削減しています。また、Goでは、if文を書くときに条件式の前に変数を宣言することができます。ここで宣言した変数はスコープがif文の中に制限されるため、条件式の中で使いたいが、if文の外では使わない変数を宣言するのに便利です。
解答例
package main import ( "fmt" ) func main() { var a, b, c, x, ans int fmt.Scan(&a, &b, &c, &x) for i := 0; i <= a; i++ { for j := 0; j <= b; j++ { if k := (x - 500*i - 100*j) / 50; 0 <= k && k <= c { ans++ } } } fmt.Println(ans) }5. ABC083B - Some Sums
ポイントは10進数の整数の取り扱いです。
「10で割った余りを取り出す」→「10で割る」を繰り返せば各桁の値を取り出すことができます。今回は、この処理を
sumOfDigits()
関数として定義しました。
Goでは、戻り値の型を指定するときに、変数名も併せて定義しておくことができます。この場合、関数内での変数宣言を省略できます。また、returnの対象を明示しない場合、定義しておいた変数をreturnします。解答例
package main import ( "fmt" ) func sumOfDigits(n int) (r int) { for n > 0 { r += n % 10 n /= 10 } return } func main() { var n, a, b, ans int fmt.Scan(&n, &a, &b) for i := 1; i <= n; i++ { if s := sumOfDigits(i); a <= s && s <= b { ans += i } } fmt.Println(ans) }6. ABC088B - Card Game for Two
ソートの問題です。
Goでは、ソートにはsort
パッケージを使用します。
int型のスライスをソートする場合、sort.Ints(slice)
またはsort.Sort(sort.IntSlice(n))
とします。
降順にソートしたい場合は、sort.Sort(sort.Reverse(sort.IntSlice(n)))
とします。降順ソートさえできてしまえば、後は貪欲法でAliceとBobに交互に得点を割り振ればOKです。
解答例
package main import ( "fmt" "sort" ) func main() { var n int fmt.Scan(&n) nums := make([]int, n) for i := range nums { fmt.Scan(&nums[i]) } var alice, bob int sort.Sort(sort.Reverse(sort.IntSlice(nums))) for i, num := range nums { if i%2 == 0 { alice += num } else { bob += num } } fmt.Println(alice - bob) }7. ABC085B - Kagami Mochi
与えられた餅の直径のユニークな値を出せばよいです。
Pythonであればset型を使えば一発でしたが、Goには集合を表す型はないのでmap型で代用します。
Goのmap型は、スライスと同様にmakeで生成します。解答例
package main import ( "fmt" ) func main() { var n int fmt.Scan(&n) m := make(map[int]bool) for i := 0; i < n; i++ { var d int fmt.Scan(&d) m[d] = true } fmt.Println(len(m)) }8. ABC085C - Otoshidama
4. と似た問題ですが、3重ループにすると時間が厳しいので2重ループで解きます。
文法的には特筆すべき点はありません。解答例
package main import "fmt" func main() { var n, y int fmt.Scan(&n, &y) for i := 0; i <= n; i++ { if i*10000 > y { break } for j := 0; j <= n-i; j++ { k := n - i - j if i*10000+j*5000+k*1000 == y { fmt.Println(i, j, k) return } } } fmt.Println(-1, -1, -1) }9. ABC049C - 白昼夢
いろいろやり方はありますが、正規表現でやるのが簡単かと思います。
Go言語ではregexp
パッケージを使います。
※ どうやら、Go言語の正規表現はあまり処理が早くないらしいです。解答例
package main import ( "fmt" "regexp" ) func main() { var s string fmt.Scan(&s) r := regexp.MustCompile(`^(dream(er)?|erase(r)?)*$`) if r.MatchString(s) { fmt.Println("YES") } else { fmt.Println("NO") } }10. ABC086C - Traveling
$t_0 = x_0 = y_0 = 0, ~ dt_i = dt_{i+1} - dt_i, ~ dx_i = |x_{i+1} - x_i|, ~ dy_i = |y_{i+1} - y_i|$ とすると、
旅行プランを実行できるのは、任意の $i ~ (0 \leq i < N)$ に対して、以下の2条件をみたすときです。
- $dx_i + dy_i \leq dt_i$
- $dx_i + dy_i \equiv dt_i \pmod 2$
絶対値の計算は
math.Abs()
がありますが、int型の引数で使いたいので今回は自作しました。解答例
package main import ( "fmt" ) func abs(n int) int { if n < 0 { return -n } return n } func main() { var n int fmt.Scan(&n) t := make([]int, n+1) x := make([]int, n+1) y := make([]int, n+1) for i := 1; i <= n; i++ { fmt.Scan(&t[i], &x[i], &y[i]) } ans := "Yes" for i := 0; i < n; i++ { dt := t[i+1] - t[i] dxy := abs(x[i+1]-x[i]) + abs(y[i+1]-y[i]) if dt < dxy || dt%2 != dxy%2 { ans = "No" break } } fmt.Println(ans) }参考
- AtCoder に登録したら次にやること ~ これだけ解けば十分闘える!過去問精選 10 問 ~
- 元ネタです。
- AtCoder Beginners Selection
- 今回の問題がコンテスト形式でまとまっているので便利。
- A Tour of Go
- Goの基本文法の学習にどうぞ。
まとめ
普段はずっとPythonで書いているので、困惑する部分もありましたがよい勉強になりました。
defer
やpanic
,go
など、競技プログラミングでは使わなそうな機能については別で勉強したいと思います。
- 投稿日:2020-02-09T09:39:49+09:00
【go + gin + gorm】webアプリにログイン機能を追加してみる
【Go+Gin+Gorm】初心者だから超簡単webサービス作ってみるの続きです。
今回はgithubに上げておきました。
https://github.com/daichiiyamada/mytweetここではユーザー登録とログイン画面を作ります。
セッションはやりません(おそらく次回)。
ログイン画面からusernameとpasswordを入力してDBに存在していたらトップ画面にリダイレクトされるようになっています。外部ライブラリのインストール
続きの方はターミナルを開いて以下を実行してください。
ここから始める方はmain.go
やcrypto/crypto.go
のimportを見てgo run
してください。// gormConnect()内で.envファイル(環境変数定義)から定数を取得するときに使います go get github.com/joho/godotenv // Usersテーブルにパスワードをそのまま保存するとセキュリティ的に危ないので、これを使って暗号化して保存します。 go get golang.org/x/crypto/bcrypt.envファイル(環境変数定義)を使用する
先ほどインストールした
godotenv
は、go言語で.envファイルを使うためのライブラリです。
ハードコーディングを避けるため、今回は使ってみようと思います。
.envファイルの中に定数を記述して、コード内でその定数を使うことができます。使い方もクソもないかもですが、こちらを参照するとわかりやすいかもです。
main.go
と同じ階層に.env
ファイルを作り、前回の続きの方は以下のように記述してください。
これらはMySQLのDB名だったり、ユーザー名だったりを記載しています。.envmytweet_DBMS=mysql mytweet_USER=test mytweet_PASS=12345678 mytweet_DBNAME=testusernameをユニークに設定する
ログインを実装するにあたって、ログインフォームから受け取ったusernameをデータベースで検索して一意なユーザーを取得したいので、usernameはユニークである必要があります。
なのでmain.go
ファイルのUser構造体を変更しました。
gorm:"unique;not null"
の部分です。
これによってアプリ側で、重複するUsernameを新たに登録しようとしてもデータベース側で弾けます。
db.AutoMigrate()
はテーブルや不足しているカラムとインデックスのみ生成します。データ保護のため、既存のカラム型の変更や未使用のカラムの削除はしないので、前回と同じテーブルを使う方、申し訳ないんですが一度Usersテーブルをtruncateしてください。ここら辺railsはActive Recordがやってくれますよね。
AutoMigrateについて詳しくは公式リファレンスを参照ください。
プロダクト向きのマイグレーションツールが他にあるそうなので気になる方は調べてみてください。今回は規模が大きくないので、これでいきます。
Go言語で使えるmigrationライブラリちなみに、プロダクト開発でgormを使おうと考えている方はこちらの記事を読んでみるといいかもしれません。
Go言語のGormを実践投入する時に最低限知っておくべきことのまとめ【ORM】main.go// User モデルの宣言 type User struct { gorm.Model Username string `form:"username" binding:"required" gorm:"unique;not null"` Password string `form:"password" binding:"required"` }ユーザー登録処理
ユーザー登録でアプリ側にさせることは、フォーム画面からユーザー名とパスワードを受け取ってDBに登録することです。
URL
localhost:8080/signup
でユーザー登録画面にいきます。トップ画面上から飛べるようにするのを忘れてました(笑)。
この画面を出すだけなら特に関数は必要ありません。
登録ボタンを押すとsignup.html
フォーム内のaction="/signup"
が/signup
にPOSTを投げるようにしています。User型の
form
で構造体を定義し、Bind
関数を使って、構造体で定義された内容と違ったデータが来てないか把握することができます。
気になる方は、GinでBindingが物珍しかったので他のフレームワークも調べてみたを読んでみるといいと思います。フォームの内容は変数
c
内に格納されていて、PostForm()を使って値を取り出します。main.go// ユーザー登録画面 router.GET("/signup", func(c *gin.Context) { c.HTML(200, "signup.html", gin.H{}) }) // ユーザー登録 router.POST("/signup", func(c *gin.Context) { var form User // バリデーション処理 if err := c.Bind(&form); err != nil { c.HTML(http.StatusBadRequest, "signup.html", gin.H{"err": err}) c.Abort() } else { username := c.PostForm("username") password := c.PostForm("password") // 登録ユーザーが重複していた場合にはじく処理 if err := createUser(username, password); err != nil { c.HTML(http.StatusBadRequest, "signup.html", gin.H{"err": err}) } c.Redirect(302, "/") } })取り出したusernameとpasswordを
createUser
関数に引数で渡します。
/crypto/crypto.go
内のPasswordEncrypt()
関数を使ってパスワードを暗号化します。
暗号化されたパスワードとユーザーネームをUsers
テーブルに保存します。package内の関数は、先頭文字を大文字にするとpublic関数になり、小文字にするとprivate関数になります。
PasswordEncrypt()
関数は先頭が大文字のPなので、public関数ですね。ユーザーが重複していたりして登録できなかったときにリダイレクトしたいので、
GetErrors()
でエラーを取得し、returnできるようにしています。
GORMのエラーハンドリングに関する記述main.go// ユーザー登録処理 func createUser(username string, password string) []error { passwordEncrypt, _ := crypto.PasswordEncrypt(password) db := gormConnect() defer db.Close() // Insert処理 if err := db.Create(&User{Username: username, Password: passwordEncrypt}).GetErrors(); err != nil { return err } return nil }ログイン処理
ログイン処理でアプリ側でさせることは、ログインフォームから受け取ったユーザー名とパスワードがDBに同じく保存されているか探すことです。
流れとしては、ログインフォームからユーザー名とパスワードを受け取る ↓ ユーザー名をもとにUsersテーブルからユーザーレコードを取得する ↓ ログインフォームから受け取ったパスワードとDBから取得したユーザーレコードのパスワードと比較 ↓ トップ画面へリダイレクトまたはログイン画面に戻るURL
localhost:8080/login
でログイン画面にいきます。これまたトップ画面上から飛べるようにするのを忘れてました(笑)。
この画面を出すだけなら特に関数は必要ありません。
登録ボタンを押すとlogin.html
フォーム内のaction="/login"
が/login
にPOSTを投げるようにしています。POSTで受け取ったユーザー名の値を
getUser()
に引数で入れ、DBからユーザーレコードを取得しています。取得したユーザーレコードはUser型として取得しているので、.Password
でパスワードを取得できます。
パスワードの比較は、CompareHashAndPassword()
の引数にdbPassword
とformPassword
を入れることで比較することができます。この関数は、crypto
ディレクトリのcrypto.go
に定義されています。この関数のreturnはerror型なので、エラー内容があればif文で引っかかるようになってます。main.go// ユーザーログイン画面 router.GET("/login", func(c *gin.Context) { c.HTML(200, "login.html", gin.H{}) }) // ユーザーログイン router.POST("/login", func(c *gin.Context) { // DBから取得したユーザーパスワード(Hash) dbPassword := getUser(c.PostForm("username")).Password log.Println(dbPassword) // フォームから取得したユーザーパスワード formPassword := c.PostForm("password") // ユーザーパスワードの比較 if err := crypto.CompareHashAndPassword(dbPassword, formPassword); err != nil { log.Println("ログインできませんでした") c.HTML(http.StatusBadRequest, "login.html", gin.H{"err": err}) c.Abort() } else { log.Println("ログインできました") c.Redirect(302, "/") } })データベースからwhere句を使ってユーザーを1件取得する際に使ったのが、
getUser
内のdb.First()
です。
db.Where()
でも代用できると思われます。他にもdbにまつわる様々な関数があるので、詳しく知りたい方は以下を読んでみるといいかと思います。
参照1
参照2db.First(&user, "username = ?", username) ↓ SELECT * FROM users WHERE username = "jinzhu";main.go// ユーザーを一件取得 func getUser(username string) User { db := gormConnect() var user User db.First(&user, "username = ?", username) db.Close() return user }起動
起動させていろいろ遊んでみましょう。
ターミナルを付けてmytweetディレクトリ内でgo run main.go
をしてください。
localhost:8080/signup
でユーザー登録
localhost:8080/login
でログイン
ですよ。
トップ画面から飛べるようにするの忘れてごめんなさい。MySQLを別タブで起動してちゃんとユーザー登録できてるか、ログインできてるか確かめてみましょう。
ちなみに、SQL文って書き方よく忘れるよね。// mysql起動 $ mysql -uroot -p // testデータベースを選択 $ use test; // usersテーブル全レコード表示 $ select * from users;各自print文(
log.Println()
)を使ってパスワードがちゃんとハッシュ化されてるかとか、ユーザー名がちゃんと取得できてるかとかみてみると勉強になるかと思います。最後に
今回は、前回のコードにユーザー登録とログイン機能を追加してみました。
次回は、デプロイしてみたり、セッション機能を追加してみたりしたいと思ってます。今回のコードはこちらから。
https://github.com/daichiiyamada/mytweetcryptoの部分はこちらのサイトのコードを流用させていただきました。
Webアプリ初心者がGo言語でサーバサイド(2. パスワード認証機能の実装)
- 投稿日:2020-02-09T02:26:09+09:00
VSCode で Go の Language Server である gopls を有効にする
gopls とは
gopls(Go Please)は Go が公式サポートしている Language Server です。現在開発が活発に進んでおり 2020年2月9日時点 での最新バージョンは v0.3.1 となっています。
VSCode の Go拡張 における Language Server について
元々 VSCode の Go拡張である microsoft/vscode-go のデフォルトの Language Server は sourcegraph/go-langserver だったのですが、go-langserver の内部で利用されていた nsf/gocode の開発終了を受け、こちら で話し合いがなされた末に 2019年3月に gopls へ 変更 となりました。
なお、2020年2月9日時点での vscode-go の最新バージョン v0.13.0 では、Language Server の使用が デフォルト無効化 となっているので使用したい場合には有効化する必要があります。
Language Server の有効化方法
マニュアル に従い VSCode で以下の作業を実施します。
VSCode に vscode-go がインストールされていない方はインストールを終えた後で実施してください。1. Language Server の有効化
Code > Preferences > Settings (cmd+,) を開き
go.useLanguageServer
を検索して有効化する。
2. gopls のインストール
View > Command Palette (cmd+shift+P) を開き
Go: Install/Update Tools
を検索した後でgopls
を選択してインストールする。
gopls v0.3.1 がインストールされました。
$ gopls version golang.org/x/tools/gopls v0.3.1 golang.org/x/tools/gopls@v0.3.1 h1:yNTWrf4gc4Or0UecjOas5pzOa3BL0WDDyKDV4Wz5VaM=3. 任意の設定を適用
こちらは任意なのでスキップしても問題ないです。
View > Command Palette (cmd+shift+P) を開き
Preferences: Open Settings (JSON)
を検索して settings.json に任意の設定を適用する。こちら を参考にして以下の設定を適用した例となります。// For gopls "go.useLanguageServer": true, "[go]": { "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.organizeImports": true, }, // Optional: Disable snippets, as they conflict with completion ranking. "editor.snippetSuggestions": "none", }, // Global settings for gopls // https://github.com/golang/tools/blob/master/gopls/doc/settings.md "gopls": { // === Officially supported Settings === // This controls the information that appears in the hover text. "hoverKind": "SynopsisDocumentation", // If true, then completion responses may contain placeholders for function parameters or struct fields. "usePlaceholders": true, // This controls where points documentation for given package in `textDocument/documentLink`. "linkTarget": "pkg.go.dev", // === Experimental Settings === // If true, it enables the use of the staticcheck.io analyzers. // Warning: This will significantly increase memory usage. "staticcheck": false, // If false, indicates that the user does not want documentation with completion results. "completionDocumentation": true, // If true, the completion engine is allowed to make suggestions for packages that you do not currently import. "completeUnimported": true, // If true, this turns on the ability to return completions from deep inside relevant entities, rather than just the locally accessible ones. "deepCompletion": true }4. 設定の適用
設定を適用するために VSCode を Reload する。これにより Language Server が有効化されます。開発が捗りそうです。
おまけ: Go のコード補完ツールの歴史
Big Sky :: gocode やめます(そして Language Server へ) がとてもわかりやすく非常に参考になったので、理解するがてら時系列を整理してみました。現時点では gopls を利用していくのが良さそうです。
- Go 1.10 以前は gocode がデファクトスタンダード
- Go 1.10 に追加されたビルドキャッシュの関係で gocode の開発が終了
- それと同時に gocode を利用していた go-langserver の開発にも影響が出た
- これらの状況を受けて gocode の fork で mdempsky/gocode や stamblerre/gocode が登場
- Go の公式 Language Server として golsp が登場(stamblerre/gocode の人が開発を担当)
- 別の Go の Language Server として saibing/bingo が登場
- golsp 開発者が bingo 開発者にコラボレーションしようと 打診 する
- golsp が gopls(Go Please) に rename される
- コラボレーションが了承されて golang/tools を fork し bingo の機能を gopls に移植した saibing/tools が登場