diff --git a/.vscode/launch.json b/.vscode/launch.json index 6c97259..092e292 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -13,7 +13,7 @@ "request": "launch", "mode": "auto", "program": "${workspaceFolder}/cmd/main.go", - "args": [ ".."] + "args": [ "."] } ] } \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go index 93a2e27..34d0787 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -17,6 +17,7 @@ func main() { hiddenFiles := flag.Bool("h", false, "show hidden files") delemitter := flag.String("d", DELEMITTER, "delemitter to use to split files when not in markdown mode must contain %s for filename") markdown := flag.Bool("m", false, "markdown mode, outputs files in markdown") + typst := flag.Bool("t", false, "typst mode, outputs files in typst") flag.Parse() cats, err := cat.Path(flag.Args()...) @@ -42,6 +43,8 @@ func main() { var out string if *markdown { out = cats.ToMarkdown() + } else if *typst { + out = cats.ToTypst() } else { out = cats.ToString(*delemitter) } diff --git a/pkg/cat/cater.go b/pkg/cat/cater.go index 07d9de8..08f9ece 100644 --- a/pkg/cat/cater.go +++ b/pkg/cat/cater.go @@ -3,14 +3,13 @@ package cat import ( "fmt" "os" - "slices" "strings" ) -type Cater map[string]string +type Cater []entry func Path(paths ...string) (c Cater, err error) { - c = make(Cater) + c = make(Cater, 0, 10) var p os.FileInfo for _, path := range paths { @@ -18,28 +17,29 @@ func Path(paths ...string) (c Cater, err error) { if err != nil { return } - + var e entry if p.IsDir() { - err = c.dir(path) + e, err = c.dir(path) } else { - err = c.file(path) + e, err = c.file(path) } if err != nil { return } + c = append(c, e) } return } func (c Cater) Ignored(ignore ignorer) Cater { - cat := make(Cater) + cat := make(Cater, 0, len(c)) + ok := func(e entry) bool { + return !ignore.Ignore(e.fqname) + } - for name, content := range c { - if ignore.Ignore(name) { - continue - } - cat[name] = content + for _, entry := range c { + cat = append(cat, entry.filter(ok)) } return cat @@ -48,16 +48,20 @@ func (c Cater) Ignored(ignore ignorer) Cater { func (c Cater) ToString(delemiter string) string { var sb strings.Builder - names := make([]string, 0, len(c)) - for name := range c { - names = append(names, name) - } + var entries []entry + entries = c - slices.Sort(names) - - for _, name := range names { - sb.WriteString(fmt.Sprintf(delemiter, name)) - sb.WriteString(c[name]) + for len(entries) > 0 { + n := make([]entry, 0, len(entries)) + for _, e := range entries { + if len(e.children) > 0 { + n = append(n, e.children...) + continue + } + sb.WriteString(fmt.Sprintf(delemiter, e.fqname)) + sb.WriteString(e.content) + } + entries = n } return sb.String() @@ -66,24 +70,67 @@ func (c Cater) ToString(delemiter string) string { func (c Cater) ToMarkdown() string { var sb strings.Builder - names := make([]string, 0, len(c)) - for name := range c { - names = append(names, name) - } - - slices.Sort(names) - - for _, name := range names { - // write header - for range strings.Count(name, "/") + 1 { + write := func(e entry, lvl int) { + for range lvl { sb.WriteString("#") } - sb.WriteString(fmt.Sprintf(" %s\n\n", name)) + sb.WriteString(fmt.Sprintf(" %s (`%s`)\n", e.name, e.fqname)) - splited := strings.Split(name, ".") + if len(e.content) > 0 { + prts := strings.Split(e.name, ".") + sb.WriteString( + fmt.Sprintf( + "```%s\n%s\n```\n\n", - // write content - sb.WriteString(fmt.Sprintf("``` %s\n%s\n```\n\n", splited[len(splited)-1], c[name])) + prts[len(prts)-1], + strings.ReplaceAll( + e.content, + "```", + "\\`\\`\\`", + ), + ), + ) + } + } + + for _, e := range c { + e.traverse(1, write) + } + + return sb.String() +} + +func (c Cater) ToTypst() string { + var sb strings.Builder + + write := func(e entry, lvl int) { + for range lvl { + sb.WriteString("=") + } + sb.WriteString(fmt.Sprintf(" %s (`%s`)\n", e.name, e.fqname)) + + if len(e.content) > 0 { + prts := strings.Split(e.name, ".") + sb.WriteString( + fmt.Sprintf( + "```%s\n%s\n```\n\n", + + prts[len(prts)-1], + strings.ReplaceAll( + e.content, + "```", + "\\`\\`\\`", + ), + ), + ) + } + } + + for _, e := range c { + + sb.WriteString("= Export\n") + sb.WriteString("#outline()\n") + e.traverse(1, write) } return sb.String() diff --git a/pkg/cat/entry.go b/pkg/cat/entry.go new file mode 100644 index 0000000..3250e65 --- /dev/null +++ b/pkg/cat/entry.go @@ -0,0 +1,32 @@ +package cat + +type entry struct { + name string + fqname string + content string + children []entry +} + +func (e entry) filter(ok func(e entry) bool) entry { + children := make([]entry, 0, len(e.children)) + for _, entry := range e.children { + if !ok(entry) { + continue + } + children = append(children, entry.filter(ok)) + } + + return entry{ + name: e.name, + fqname: e.fqname, + content: e.content, + children: children, + } +} + +func (e entry) traverse(lvl int, do func(e entry, lvl int)) { + do(e, lvl) + for _, entry := range e.children { + entry.traverse(lvl+1, do) + } +} diff --git a/pkg/cat/internal.go b/pkg/cat/internal.go index ecf688c..60c862e 100644 --- a/pkg/cat/internal.go +++ b/pkg/cat/internal.go @@ -8,32 +8,41 @@ import ( "strings" ) -func (c Cater) dir(dir string) error { +func (c Cater) dir(dir string) (e entry, err error) { files, err := os.ReadDir(dir) if err != nil { - return err + return } + e.fqname = dir + e.name = name(dir) + e.children = []entry{} + for _, file := range files { i, err := file.Info() if err != nil { - continue + return e, err } path := path.Join(dir, i.Name()) + var ent entry if !file.IsDir() { - c.file(path) + ent, err = c.file(path) } else { - c.dir(path) + ent, err = c.dir(path) } + if err != nil { + return e, err + } + e.children = append(e.children, ent) } - return nil + return } -func (c Cater) file(filePath string) error { +func (c Cater) file(filePath string) (e entry, err error) { file, err := os.Open(filePath) if err != nil { - return err + return } defer file.Close() @@ -44,7 +53,7 @@ func (c Cater) file(filePath string) error { for { line, err := reader.ReadString('\n') if err != nil && err != io.EOF { - return err + return e, err } sb.WriteString(line) if err == io.EOF { @@ -52,7 +61,15 @@ func (c Cater) file(filePath string) error { } } - c[filePath] = sb.String() + e.fqname = filePath + e.name = name(filePath) + e.content = sb.String() + e.children = []entry{} - return nil + return +} + +func name(name string) string { + ps := strings.Split(name, "/") + return ps[len(ps)-1] } diff --git a/pkg/ignore/filesystem.go b/pkg/ignore/filesystem.go index 6ff926b..af5ce15 100644 --- a/pkg/ignore/filesystem.go +++ b/pkg/ignore/filesystem.go @@ -1,9 +1,12 @@ package ignore -import "strings" +import ( + "strings" +) type Filesystem struct{} func (f Filesystem) Ignore(name string) bool { - return strings.Contains(name, "/.") || strings.HasPrefix(name, ".") + parts := strings.Split(name, "/") + return strings.HasPrefix(parts[len(parts)-1], ".") }