Skip to content

Commit 76966de

Browse files
committed
feat: add template
1 parent 2e4175d commit 76966de

File tree

5 files changed

+359
-3
lines changed

5 files changed

+359
-3
lines changed

Diff for: .cursor/template..yaml

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
templates:
2+
- name: leetcode-readme
3+
path: Leetcode/{{id}}.{{title}}/README.md
4+
content: |
5+
---
6+
title: {{id}}.{{title}}
7+
subtitle: "https://leetcode.com/problems/{{title | dash}}/description/"
8+
date: {{now}}
9+
lastmod: {{now}}
10+
draft: false
11+
author: "Kimi.Tsai"
12+
authorLink: "https://kimi0230.github.io/"
13+
description: "{{title | space}}"
14+
license: ""
15+
images: []
16+
17+
tags: [LeetCode, Go, {{difficulty}}, {{tags}}]
18+
categories: [LeetCode]
19+
20+
featuredImage: ""
21+
featuredImagePreview: ""
22+
23+
hiddenFromHomePage: false
24+
hiddenFromSearch: false
25+
twemoji: false
26+
lightgallery: true
27+
ruby: true
28+
fraction: true
29+
fontawesome: true
30+
linkToMarkdown: false
31+
rssFullText: false
32+
33+
toc:
34+
enable: true
35+
auto: true
36+
code:
37+
copy: true
38+
maxShownLines: 200
39+
math:
40+
enable: false
41+
mapbox:
42+
share:
43+
enable: true
44+
comment:
45+
enable: true
46+
library:
47+
css:
48+
js:
49+
seo:
50+
images: []
51+
---
52+
53+
# [{{id}}.{{title}}](https://leetcode.com/problems/{{title | dash}}/description/)
54+
55+
## 題目
56+
{{problem}}
57+
58+
## 題目大意
59+
60+
61+
## 解題思路
62+
63+
64+
## 來源
65+
* https://leetcode.com/problems/{{title | dash}}/description/
66+
67+
## 解答
68+
https://github.com/kimi0230/LeetcodeGolang/blob/master/Leetcode/{{id}}.{{title}}/main.go
69+
70+
```go
71+
{{code}}
72+
```
73+
74+
## Benchmark
75+
76+
```sh
77+
78+
```

Diff for: .leetcode_problems_cache.json

+1
Large diffs are not rendered by default.

Diff for: .vscode/launch.json

+23-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,27 @@
1010
"request": "launch",
1111
"mode": "auto",
1212
"program": "${fileDirname}"
13-
}
13+
},
14+
{
15+
"name": "執行 leetcode-readme-gen",
16+
"type": "go",
17+
"request": "launch",
18+
"mode": "debug",
19+
"program": "${workspaceFolder}/cmd/leetcode-readme-gen.go",
20+
"cwd": "${workspaceFolder}",
21+
"args": [
22+
"1"
23+
],
24+
"env": {},
25+
"dlvFlags": [
26+
"--check-go-version=false",
27+
"--only-same-user=false"
28+
],
29+
"showLog": true,
30+
"asRoot": false,
31+
"trace": "verbose",
32+
"stopOnEntry": true,
33+
"debugAdapter": "dlv-dap"
34+
},
1435
]
15-
}
36+
}

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1200,4 +1200,4 @@ func search(nums []int, target int) int {
12001200
* [Codility](https://app.codility.com/programmers/)
12011201
* [GitHub: labuladong/fucking-algorithm(labuladong 算法小抄)](https://github.com/labuladong/fucking-algorithm)
12021202
* https://cses.fi/
1203-
* https://github.com/neetcode-gh/leetcode
1203+
* https://github.com/neetcode-gh/leetcode

Diff for: cmd/leetcode-readme-gen.go

+256
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"errors"
7+
"fmt"
8+
"io"
9+
"net/http"
10+
"os"
11+
"path/filepath"
12+
"strconv"
13+
"strings"
14+
"text/template"
15+
"time"
16+
)
17+
18+
type LeetcodeResponse struct {
19+
Data struct {
20+
Question struct {
21+
QuestionID string `json:"questionId"`
22+
Title string `json:"title"`
23+
Content string `json:"content"`
24+
Difficulty string `json:"difficulty"`
25+
Tags []struct {
26+
Name string `json:"name"`
27+
} `json:"topicTags"`
28+
} `json:"question"`
29+
} `json:"data"`
30+
}
31+
32+
type ProblemListResponse struct {
33+
StatStatusPairs []struct {
34+
Stat struct {
35+
QuestionID int `json:"question_id"`
36+
QuestionSlug string `json:"question__title_slug"`
37+
} `json:"stat"`
38+
} `json:"stat_status_pairs"`
39+
}
40+
41+
const readmeTemplate = `---
42+
title: {{.ID}}.{{.Title}}
43+
subtitle: "https://leetcode.com/problems/{{.Slug}}/description/"
44+
date: {{.Now}}
45+
lastmod: {{.Now}}
46+
draft: false
47+
author: "Kimi.Tsai"
48+
authorLink: "https://kimi0230.github.io/"
49+
description: "{{.Title}}"
50+
license: ""
51+
images: []
52+
53+
tags: [LeetCode, Go, {{.Difficulty}}{{range .Tags}}, {{.}}{{end}}]
54+
categories: [LeetCode]
55+
56+
featuredImage: ""
57+
featuredImagePreview: ""
58+
59+
hiddenFromHomePage: false
60+
hiddenFromSearch: false
61+
twemoji: false
62+
lightgallery: true
63+
ruby: true
64+
fraction: true
65+
fontawesome: true
66+
linkToMarkdown: false
67+
rssFullText: false
68+
69+
toc:
70+
enable: true
71+
auto: true
72+
code:
73+
copy: true
74+
maxShownLines: 200
75+
math:
76+
enable: false
77+
mapbox:
78+
share:
79+
enable: true
80+
comment:
81+
enable: true
82+
library:
83+
css:
84+
js:
85+
seo:
86+
images: []
87+
---
88+
89+
# [{{.ID}}.{{.Title}}](https://leetcode.com/problems/{{.Slug}}/description/)
90+
91+
## 題目
92+
{{.Problem}}
93+
94+
## 題目大意
95+
96+
97+
## 解題思路
98+
99+
100+
## 來源
101+
* https://leetcode.com/problems/{{.Slug}}/description/
102+
103+
## 解答
104+
https://github.com/kimi0230/LeetcodeGolang/blob/master/Leetcode/{{.ID}}.{{.Title}}/main.go
105+
106+
107+
108+
## Benchmark
109+
110+
111+
`
112+
113+
type ReadmeData struct {
114+
ID string
115+
Title string
116+
Slug string
117+
Difficulty string
118+
Tags []string
119+
Problem string
120+
Now string
121+
}
122+
123+
func fetchSlugFromID(id int) (string, error) {
124+
cacheFile := ".leetcode_problems_cache.json"
125+
var problems ProblemListResponse
126+
127+
if content, err := os.ReadFile(cacheFile); err == nil {
128+
_ = json.Unmarshal(content, &problems)
129+
} else {
130+
resp, err := http.Get("https://leetcode.com/api/problems/all/")
131+
if err != nil {
132+
return "", err
133+
}
134+
defer resp.Body.Close()
135+
body, _ := io.ReadAll(resp.Body)
136+
if err := json.Unmarshal(body, &problems); err != nil {
137+
return "", err
138+
}
139+
_ = os.WriteFile(cacheFile, body, 0644)
140+
}
141+
142+
for _, p := range problems.StatStatusPairs {
143+
if p.Stat.QuestionID == id {
144+
return p.Stat.QuestionSlug, nil
145+
}
146+
}
147+
return "", errors.New("slug not found for id")
148+
}
149+
150+
func fetchLeetcode(slug string) (*ReadmeData, error) {
151+
query := `query questionTitleSlug($titleSlug: String!) {
152+
question(titleSlug: $titleSlug) {
153+
questionId
154+
title
155+
content
156+
difficulty
157+
topicTags {
158+
name
159+
}
160+
}
161+
}`
162+
163+
payload := map[string]interface{}{
164+
"query": query,
165+
"variables": map[string]string{"titleSlug": slug},
166+
}
167+
jsonPayload, _ := json.Marshal(payload)
168+
169+
resp, err := http.Post("https://leetcode.com/graphql", "application/json", bytes.NewBuffer(jsonPayload))
170+
if err != nil {
171+
return nil, err
172+
}
173+
defer resp.Body.Close()
174+
body, _ := io.ReadAll(resp.Body)
175+
176+
var result LeetcodeResponse
177+
if err := json.Unmarshal(body, &result); err != nil {
178+
return nil, err
179+
}
180+
181+
q := result.Data.Question
182+
tags := []string{}
183+
for _, tag := range q.Tags {
184+
tags = append(tags, tag.Name)
185+
}
186+
187+
return &ReadmeData{
188+
ID: q.QuestionID,
189+
Title: strings.ReplaceAll(q.Title, " ", "-"),
190+
Slug: slug,
191+
Difficulty: q.Difficulty,
192+
Tags: tags,
193+
Problem: stripHTML(q.Content),
194+
Now: time.Now().Format(time.RFC3339),
195+
}, nil
196+
}
197+
198+
func stripHTML(input string) string {
199+
var output strings.Builder
200+
inTag := false
201+
for _, r := range input {
202+
switch r {
203+
case '<':
204+
inTag = true
205+
case '>':
206+
inTag = false
207+
default:
208+
if !inTag {
209+
output.WriteRune(r)
210+
}
211+
}
212+
}
213+
return strings.TrimSpace(output.String())
214+
}
215+
216+
func writeREADME(data *ReadmeData) error {
217+
dir := fmt.Sprintf("Leetcode/%s.%s", data.ID, data.Title)
218+
os.MkdirAll(dir, 0755)
219+
f, err := os.Create(filepath.Join(dir, "README.md"))
220+
if err != nil {
221+
return err
222+
}
223+
defer f.Close()
224+
tmpl, err := template.New("readme").Parse(readmeTemplate)
225+
if err != nil {
226+
return err
227+
}
228+
return tmpl.Execute(f, data)
229+
}
230+
231+
func main() {
232+
if len(os.Args) < 2 {
233+
fmt.Println("Usage: leetcode-readme-gen <id>")
234+
return
235+
}
236+
237+
idNum, err := strconv.Atoi(os.Args[1])
238+
if err != nil {
239+
fmt.Println("Please provide a valid Leetcode problem number (e.g. 217)")
240+
return
241+
}
242+
243+
slug, err := fetchSlugFromID(idNum)
244+
if err != nil {
245+
panic(err)
246+
}
247+
248+
data, err := fetchLeetcode(slug)
249+
if err != nil {
250+
panic(err)
251+
}
252+
if err := writeREADME(data); err != nil {
253+
panic(err)
254+
}
255+
fmt.Printf("README generated for %s (%s)\n", data.Title, data.ID)
256+
}

0 commit comments

Comments
 (0)