💻

LeetCodeをVS CodeとGoで解くための環境づくり

2022/10/09に公開

ずっとほったらかしていたLeetCodeをGoで解くための環境を作ったのでその備忘録です。

この記事に関連したコードは、このリポジトリにあります。

LeetCodeは普通に使うと、ブラウザでコードを書いて、それをSubmitすることで動作確認できます。入力補完がないし、コンパイルエラーにも気付きにくいし、つらい。

そのためVS CodeでLeetCodeのコードを自動生成して、VS CodeからLeetCodeにSubmitできるようにしました。

仕事ではMacで開発しているのですが、プライベートではWindowsマシンでコードを書いていたりします。最近はWindowsの開発環境も充実していていいですね。

今回のLeetCode開発環境はこんな感じです。

Windows 11のWSL(Windows Subsystem for Linux)を利用してUbuntu上で開発しています。今回はWSLなどには触れません。ずっと前に試行錯誤して環境整備したので、すっかり忘れてしまいました。

VS Codeで開発しているので、Macでもだいたい同じような環境は作れると思います。たぶん。

ところで、上記プラグインを使った環境構築については、すでにとても分かりやす記事があります。なのでまずはこちら読みましょう。

使い方

インストール

VS Codeにプラグインをインストールしましょう。

Requirementsとして以下の記載があります。Node.jsが必要になるので事前にインストールしましょう。

  • VS Code 1.30.1+
  • Node.js 10+

ログイン

VS Codeにプラグインをインストールしたら、まずはログインします。

公式サイトにはログインで問題が起きていると書いてありました。

Recently we observed that the extension cannot login to leetcode.com endpoint anymore. The root cause of this issue is that leetcode.com changed its login mechanism and so far there is no ideal way to fix that issue.

Thanks for @yihong0618 provided a workaround which can somehow mitigate this. Now you can simply click the Sign In button and then select Third Party login or Cookie login.

僕もLeetCodeのアカウントでログインできなかったので、GitHubアカウントでログインしました。

ログインはコマンドパレットから LeetCode Sign In を選び、その後 Third-Party: GitHubを選んでログインしました。

プラグインの設定

ファイル > ユーザー設定 > 設定 から設定ができます。

以下の3点を変更しました。

  • Default Language: golang
    • Goで書きたいので
  • File Path
    • Goっぽいファイル名(Snake Case)にしたいので
  • Workspace Folder: /home/sadah/git/leetcode/code
    • leetcodeというリポジトリを作って、その中のcodeというディレクトリにファイルを置きたいので

File Pathはsetting.jsonを編集します。

    "leetcode.filePath": {
        "default": {
            "folder": "",
            "filename": "${difficulty}_${id}_${snake_case_name}.${ext}"
        }
    },

File Pathは柔軟な設定ができて、いい感じです。

デフォルトでは "filename": "${id}.${kebab-case-name}.${ext}" となっていました。1.two-sum.go みたいなファイル名になります。

個人的にdifficultyを入れたかったのと、Snake Caseを使いたかったので修正しました。僕の設定だと easy_1_two_sum.go みたいなファイル名になります。

ディレクトリ構成

いろいろ試して、こんな感じに落ち着きました。

leetcode
|-- README.md
|-- code
|   |-- easy_1480_running_sum_of_1d_array.go
|   |-- easy_1_two_sum.go
|   |-- easy_205_isomorphic_strings.go
|   |-- easy_205_isomorphic_strings_test.go
|   |-- easy_21_merge_two_sorted_lists.go
|   |-- easy_21_merge_two_sorted_lists_test.go
|   |-- easy_392_is_subsequence.go
|   |-- easy_392_is_subsequence_test.go
|   |-- easy_724_find_pivot_index.go
|   |-- easy_9_palindrome_number.go
|   `-- utils
|       `-- utils.go
|-- go.mod
`-- go.sum

問題を選ぶ

Pluginから問題を選択して Code Now から解けます。

Pluginから問題を選択

問題の番号や名前が分かっている場合は、コマンドパレットから探すのも便利です。LeetCode: Search Problem で検索できます。

LeetCode: Search Problemで問題を検索

問題を解く

問題を選択すると、以下のようなコードが自動生成されます。このままだとエラーやWarningがでます。

/*
 * @lc app=leetcode id=1 lang=golang
 *
 * [1] Two Sum
 */

// @lc code=start
func twoSum(nums []int, target int) []int {
    
}
// @lc code=end

あとはとりあえずPackage宣言して、戻り値ある場合はreturnを付けて、エラーのない状態にします。

あとはlintのWarning出てきます。まあ unused だよね。

func twoSum is unused (U1000)go-staticcheck

気になっちゃうので //lint:ignore U1000 //を付けて消します。

こんな感じに修正してから、コードを書き始めています。

/*
 * @lc app=leetcode id=1 lang=golang
 *
 * [1] Two Sum
 */

package code

// @lc code=start
//
//lint:ignore U1000 //
func twoSum(nums []int, target int) []int {
	return nil
}

// @lc code=end

コードを書いたら Submit / Test からLeetCodeに送信できます。これが便利。

問題によっては、こんな感じで利用する構造体がコメントアウトされていることがあります。

/*
 * @lc app=leetcode id=21 lang=golang
 *
 * [21] Merge Two Sorted Lists
 */

// @lc code=start
/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func mergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode {
    
}
// @lc code=end

どうやら送信されるコードは // @lc code=start// @lc code=end の範囲内のようでした。そのため上記の範囲外で構造体を定義すれば、ローカルの環境でも利用できます。

/*
 * @lc app=leetcode id=21 lang=golang
 *
 * [21] Merge Two Sorted Lists
 */

// Definition for singly-linked list.
type ListNode struct {
	Val int
	Next *ListNode
}

// @lc code=start
func mergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode {
	
}
// @lc code=end

ローカルで動作確認したいときは、VS CodeでUnit Testをジェネレートしています。

あと、どういった問題を解いたらいいのかよくわからなかったので、とりあえずこちらをやっています。

はまったこと

主にbrewではまりました…。Macと同じようにパッケージを管理するため、普段はhomebrewを使っています。

GoのバージョンアップやNode.jsのインストールで Too Many Open Files がでて、環境がぶっ壊れました。

その結果かわからないけど、まずは brew update がエラーになってどうしようもないので brew update-reset しました。

Too Many Open Files については設定を確認してみると、Maximum number of open file descriptorsが1024でした。

❯ ulimit -a
Maximum size of core files created                              (kB, -c) 0
Maximum size of a process’s data segment                        (kB, -d) unlimited
Control of maximum nice priority                                    (-e) 0
Maximum size of files created by the shell                      (kB, -f) unlimited
Maximum number of pending signals                                   (-i) 128096
Maximum size that may be locked into memory                     (kB, -l) 65536
Maximum resident set size                                       (kB, -m) unlimited
Maximum number of open file descriptors                             (-n) 1024
Maximum bytes in POSIX message queues                           (kB, -q) 800
Maximum realtime scheduling priority                                (-r) 0
Maximum stack size                                              (kB, -s) 8192
Maximum amount of CPU time in seconds                      (seconds, -t) unlimited
Maximum number of processes available to current user               (-u) 128096
Maximum amount of virtual memory available to each process      (kB, -v) unlimited
Maximum contiguous realtime CPU time                                (-y) unlimited

これは厳しいのでとりあえず大きくします。

❯ ulimit -n 65536

brewはreinstallがないので、その後壊れたパッケージのuninstall / installで入れなおします。

途中で普段使っている fish も壊れて、WSLのTerminalを立ち上げることができなくなったりしました。仕方がないのでPowerShellから wsl /bin/bash とかやって、そこからfishの再インストールをしました。やれやれ。

まとめ

2年くらい前にAtCoderをやっていて、緑に到達したところで止まってしまっていました。AtCoderは online-judge-toolsatcoder-cli を使って、今回と同じようなローカルからSubmitできるような環境で解いていたので、LeetCodeもそんな環境を作りたいと思いつつ、時間だけが過ぎ去っていました。

今回、かなり快適にLeetCodeを解く環境ができあがりました。あとはちゃんと解いていくだけ…。

Refs

この記事を書く上で、以下の記事を参考にさせていただきました。ありがとうございます。

Discussion