add typst export

This commit is contained in:
u80864958
2025-05-07 11:00:32 +02:00
parent c0fe9a4a46
commit 454554f8e0
6 changed files with 151 additions and 49 deletions

2
.vscode/launch.json vendored
View File

@ -13,7 +13,7 @@
"request": "launch", "request": "launch",
"mode": "auto", "mode": "auto",
"program": "${workspaceFolder}/cmd/main.go", "program": "${workspaceFolder}/cmd/main.go",
"args": [ ".."] "args": [ "."]
} }
] ]
} }

View File

@ -17,6 +17,7 @@ func main() {
hiddenFiles := flag.Bool("h", false, "show hidden files") 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") 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") markdown := flag.Bool("m", false, "markdown mode, outputs files in markdown")
typst := flag.Bool("t", false, "typst mode, outputs files in typst")
flag.Parse() flag.Parse()
cats, err := cat.Path(flag.Args()...) cats, err := cat.Path(flag.Args()...)
@ -42,6 +43,8 @@ func main() {
var out string var out string
if *markdown { if *markdown {
out = cats.ToMarkdown() out = cats.ToMarkdown()
} else if *typst {
out = cats.ToTypst()
} else { } else {
out = cats.ToString(*delemitter) out = cats.ToString(*delemitter)
} }

View File

@ -3,14 +3,13 @@ package cat
import ( import (
"fmt" "fmt"
"os" "os"
"slices"
"strings" "strings"
) )
type Cater map[string]string type Cater []entry
func Path(paths ...string) (c Cater, err error) { func Path(paths ...string) (c Cater, err error) {
c = make(Cater) c = make(Cater, 0, 10)
var p os.FileInfo var p os.FileInfo
for _, path := range paths { for _, path := range paths {
@ -18,28 +17,29 @@ func Path(paths ...string) (c Cater, err error) {
if err != nil { if err != nil {
return return
} }
var e entry
if p.IsDir() { if p.IsDir() {
err = c.dir(path) e, err = c.dir(path)
} else { } else {
err = c.file(path) e, err = c.file(path)
} }
if err != nil { if err != nil {
return return
} }
c = append(c, e)
} }
return return
} }
func (c Cater) Ignored(ignore ignorer) Cater { 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 { for _, entry := range c {
if ignore.Ignore(name) { cat = append(cat, entry.filter(ok))
continue
}
cat[name] = content
} }
return cat return cat
@ -48,16 +48,20 @@ func (c Cater) Ignored(ignore ignorer) Cater {
func (c Cater) ToString(delemiter string) string { func (c Cater) ToString(delemiter string) string {
var sb strings.Builder var sb strings.Builder
names := make([]string, 0, len(c)) var entries []entry
for name := range c { entries = c
names = append(names, name)
}
slices.Sort(names) for len(entries) > 0 {
n := make([]entry, 0, len(entries))
for _, name := range names { for _, e := range entries {
sb.WriteString(fmt.Sprintf(delemiter, name)) if len(e.children) > 0 {
sb.WriteString(c[name]) n = append(n, e.children...)
continue
}
sb.WriteString(fmt.Sprintf(delemiter, e.fqname))
sb.WriteString(e.content)
}
entries = n
} }
return sb.String() return sb.String()
@ -66,24 +70,67 @@ func (c Cater) ToString(delemiter string) string {
func (c Cater) ToMarkdown() string { func (c Cater) ToMarkdown() string {
var sb strings.Builder var sb strings.Builder
names := make([]string, 0, len(c)) write := func(e entry, lvl int) {
for name := range c { for range lvl {
names = append(names, name)
}
slices.Sort(names)
for _, name := range names {
// write header
for range strings.Count(name, "/") + 1 {
sb.WriteString("#") 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 prts[len(prts)-1],
sb.WriteString(fmt.Sprintf("``` %s\n%s\n```\n\n", splited[len(splited)-1], c[name])) 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() return sb.String()

32
pkg/cat/entry.go Normal file
View File

@ -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)
}
}

View File

@ -8,32 +8,41 @@ import (
"strings" "strings"
) )
func (c Cater) dir(dir string) error { func (c Cater) dir(dir string) (e entry, err error) {
files, err := os.ReadDir(dir) files, err := os.ReadDir(dir)
if err != nil { if err != nil {
return err return
} }
e.fqname = dir
e.name = name(dir)
e.children = []entry{}
for _, file := range files { for _, file := range files {
i, err := file.Info() i, err := file.Info()
if err != nil { if err != nil {
continue return e, err
} }
path := path.Join(dir, i.Name()) path := path.Join(dir, i.Name())
var ent entry
if !file.IsDir() { if !file.IsDir() {
c.file(path) ent, err = c.file(path)
} else { } 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) file, err := os.Open(filePath)
if err != nil { if err != nil {
return err return
} }
defer file.Close() defer file.Close()
@ -44,7 +53,7 @@ func (c Cater) file(filePath string) error {
for { for {
line, err := reader.ReadString('\n') line, err := reader.ReadString('\n')
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
return err return e, err
} }
sb.WriteString(line) sb.WriteString(line)
if err == io.EOF { 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]
} }

View File

@ -1,9 +1,12 @@
package ignore package ignore
import "strings" import (
"strings"
)
type Filesystem struct{} type Filesystem struct{}
func (f Filesystem) Ignore(name string) bool { 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], ".")
} }