summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/kdev/main.go108
1 files changed, 108 insertions, 0 deletions
diff --git a/cmd/kdev/main.go b/cmd/kdev/main.go
new file mode 100644
index 0000000..9efdee2
--- /dev/null
+++ b/cmd/kdev/main.go
@@ -0,0 +1,108 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io/fs"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "slices"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/martinlehoux/kagamigo/kcore"
+ "github.com/samber/lo"
+)
+
+type Record struct {
+ keyword string
+ path string
+ line int
+ date time.Time
+}
+
+func main() {
+ var err error
+ sortBy := flag.String("sort", "date", "Sort by date or random")
+ maxRecords := flag.Int("max", 25, "Maximum number of records to display")
+ repoPath := flag.String("repo", ".", "Path to the repository")
+ afterS := flag.String("after", "2022-09-01", "Only show records after this date")
+ flag.Parse()
+ after, err := time.Parse(time.DateOnly, *afterS)
+ kcore.Expect(err, "Error parsing date")
+ excludes := []string{".venv", ".git", ".ruff_cache", "db_dumps", ".mypy_cache"}
+ keywords := []string{"# TODO"}
+ records := []Record{}
+ // repo, err := git.PlainOpen(root)
+ kcore.Expect(err, "Error opening repository")
+ // ref, err := repo.Head()
+ kcore.Expect(err, "Error getting HEAD")
+ // commit, err := repo.CommitObject(ref.Hash())
+ kcore.Expect(err, "Error getting commit object")
+ err = filepath.WalkDir(*repoPath, func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ if slices.Contains(excludes, d.Name()) {
+ return filepath.SkipDir
+ }
+ if d.Type()&fs.ModeSymlink != 0 {
+ return nil
+ }
+ if !d.IsDir() {
+ return processFile(path, keywords, &records, *repoPath)
+ }
+ return nil
+ })
+ kcore.Expect(err, "Error walking directory")
+ records = lo.Filter(records, func(record Record, index int) bool { return record.date.After(after) })
+ if *sortBy == "random" {
+ records = lo.Shuffle(records)
+ } else {
+ slices.SortFunc(records, func(a, b Record) int { return -int(a.date.Sub(b.date).Nanoseconds()) })
+ }
+ for _, record := range records[:*maxRecords] {
+ fmt.Printf("%s\t %s:%d\n", record.date.Format("2006-01-02"), record.path, record.line)
+ }
+}
+
+func processFile(path string, keywords []string, records *[]Record, repoPath string) error {
+ content, err := os.ReadFile(path) // #nosec G304
+ if err != nil {
+ return err
+ }
+ lines := strings.Split(string(content), "\n")
+ for i, line := range lines {
+ for _, keyword := range keywords {
+ if strings.Contains(line, keyword) {
+ *records = append(*records, extractRecord(keyword, path, i+1, repoPath))
+ }
+ }
+ }
+ return nil
+}
+
+func extractRecord(keyword string, path string, line int, repoPath string) Record {
+ record := Record{
+ keyword: keyword,
+ path: path,
+ line: line,
+ }
+ cmd := exec.Command("git", "blame", "-L", fmt.Sprintf("%d,%d", record.line, record.line), "--porcelain", path) // #nosec G204
+ cmd.Dir = repoPath
+ output, err := cmd.Output()
+ if err != nil {
+ panic(kcore.Wrap(err, "Error running git blame"))
+ }
+ for _, blameLine := range strings.Split(string(output), "\n") {
+ if strings.HasPrefix(blameLine, "author-time ") {
+ timestamp, err := strconv.ParseInt(strings.TrimPrefix(blameLine, "author-time "), 10, 64)
+ kcore.Expect(err, "Error parsing timestamp")
+ record.date = time.Unix(timestamp, 0)
+ break
+ }
+ }
+ return record
+}