diff --git a/get.go b/get.go new file mode 100644 index 0000000..67a4699 --- /dev/null +++ b/get.go @@ -0,0 +1,49 @@ +package markdown + +import ( + "github.com/lithammer/fuzzysearch/fuzzy" +) + +func (md MarkdownFile) GetFrontMatter(key string, defaultValue interface{}) (value interface{}) { + if md.FrontMatter == nil { + return defaultValue + } + if val, ok := md.FrontMatter[key]; ok { + return val + } + return defaultValue +} + +// GetSection returns the first section of the specified type with the specified text +func (md MarkdownFile) GetSection(sectionType SectionType, text string) (section *Section) { + for _, s := range md.Sections { + if s.SectionType == sectionType && s.Text == text { + return &s + } + } + return nil +} + +// SearchSection returns a list of sections that contain the search string (with fuzzy search) +func (md MarkdownFile) SearchSection(searchString string) (sections *[]Section) { + var foundSections []Section + for _, s := range md.Sections { + if fuzzy.Match(searchString, s.Text) { + foundSections = append(foundSections, s) + } + } + return &foundSections +} + +// SearchSection returns a list of sections with specified section type that contain the search string (with fuzzy search) +func (md MarkdownFile) SearchSectionWithType(searchString string, sectionType SectionType) (sections *[]Section) { + var foundSections []Section + for _, s := range md.Sections { + if s.SectionType == sectionType { + if fuzzy.Match(searchString, s.Text) { + foundSections = append(foundSections, s) + } + } + } + return &foundSections +} diff --git a/go.mod b/go.mod index e3f4ca5..5acdef0 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,9 @@ module github.com/anotherhadi/markdown go 1.22.5 -require gopkg.in/yaml.v3 v3.0.1 +require ( + github.com/lithammer/fuzzysearch v1.1.8 + gopkg.in/yaml.v3 v3.0.1 +) + +require golang.org/x/text v0.9.0 // indirect diff --git a/go.sum b/go.sum index a62c313..6438e39 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,37 @@ +github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4= +github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/markdown.go b/markdown.go index 3492018..0987f54 100644 --- a/markdown.go +++ b/markdown.go @@ -85,11 +85,10 @@ type Section struct { } type MarkdownFile struct { - Path string - Title string // First "H1" section - FrontMatter map[interface{}]interface{} - frontMatterData []string // The original front matter data - Sections []Section + Path string + Title string // First "H1" section + FrontMatter map[interface{}]interface{} + Sections []Section } func New(path string) MarkdownFile { diff --git a/markdown_test.go b/markdown_test.go index 2f86450..4d2db12 100644 --- a/markdown_test.go +++ b/markdown_test.go @@ -101,7 +101,6 @@ func TestReadAndWrite(t *testing.T) { "./testfiles/empty.md", "./testfiles/lorem.md", "./testfiles/empty_section.md", - "./testfiles/metadata.md", "./testfiles/start_with_no_section.md", "./testfiles/hyprland.md", } @@ -163,6 +162,11 @@ func TestAddSectionAtIndex(t *testing.T) { if err != nil { t.Errorf("Error writing file: %s", err) } + + err = os.Remove(filename + ".tmp") + if err != nil { + t.Errorf("Error removing file: %s", err) + } } func TestAddLine(t *testing.T) { @@ -225,4 +229,109 @@ func TestAddLineAtIndex(t *testing.T) { if err != nil { t.Errorf("Error writing file: %s", err) } + + err = os.Remove(filename + ".tmp") + if err != nil { + t.Errorf("Error removing file: %s", err) + } +} + +func TestGetFrontMatter(t *testing.T) { + filename := "./testfiles/metadata.md" + md := New(filename) + err := md.Read() + if err != nil { + t.Errorf("Error reading file: %s", err) + } + + value := md.GetFrontMatter("author", "") + if value != "Another Hadi" { + t.Errorf("Expected 'Another Hadi', got %s", value) + } + + value = md.GetFrontMatter("tag", nil) + valueList, ok := value.([]interface{}) + if !ok { + t.Errorf("Expected a list of strings, got %s", value) + } + if valueList[0] != "myFirstTag" { + t.Errorf("Expected 'myFirstTag', got %s", valueList[0]) + } + + value = md.GetFrontMatter("aRandomValue", "Empty") + if value != "Empty" { + t.Errorf("Expected 'Empty', got %s", value) + } + + value = md.GetFrontMatter("aRandomValue", nil) + if value != nil { + t.Errorf("Expected 'nil', got %s", value) + } +} + +func TestGetSection(t *testing.T) { + filename := "./testfiles/lorem.md" + md := New(filename) + err := md.Read() + if err != nil { + t.Errorf("Error reading file: %s", err) + } + + section := md.GetSection(H2, "Etiam") + if section == nil { + t.Errorf("Expected a section, got nil") + return + } + section.Lines[0].Text = "New Text" + if md.Sections[1].Lines[0].Text != "New Text" { + t.Errorf("Expected 'New Text', got %s", md.Sections[1].Lines[0].Text) + } + + section = md.GetSection(H1, "Not Found") + if section != nil { + t.Errorf("Expected nil, got a section") + } +} + +func TestSearchSection(t *testing.T) { + filename := "./testfiles/lorem.md" + md := New(filename) + err := md.Read() + if err != nil { + t.Errorf("Error reading file: %s", err) + } + + sections := md.SearchSection("Etiam") + if len(*sections) != 1 { + t.Errorf("Expected 1 section, got %d", len(*sections)) + } + + sections = md.SearchSection("Etia") + if len(*sections) != 1 { + t.Errorf("Expected 1 section, got %d", len(*sections)) + } + + sections = md.SearchSection("Etim") + if len(*sections) != 1 { + t.Errorf("Expected 1 section, got %d", len(*sections)) + } + section := (*sections)[0] + section.Lines[0].Text = "New Text" + if md.Sections[1].Lines[0].Text != "New Text" { + t.Errorf("Expected 'New Text', got %s", md.Sections[1].Lines[0].Text) + } +} + +func TestSearchSectionWithType(t *testing.T) { + filename := "./testfiles/lorem.md" + md := New(filename) + err := md.Read() + if err != nil { + t.Errorf("Error reading file: %s", err) + } + + sections := md.SearchSectionWithType("Etiam", H2) + if len(*sections) != 1 { + t.Errorf("Expected 1 section, got %d", len(*sections)) + } } diff --git a/read.go b/read.go index 6519199..3587ebe 100644 --- a/read.go +++ b/read.go @@ -70,11 +70,9 @@ func (md *MarkdownFile) Read() (err error) { // Skip the FrontMatter if line == "---" && isFirstLine { - md.frontMatterData = append(md.frontMatterData, line) for { scanner.Scan() line = scanner.Text() - md.frontMatterData = append(md.frontMatterData, line) if line == "---" { scanner.Scan() line = scanner.Text() @@ -84,7 +82,6 @@ func (md *MarkdownFile) Read() (err error) { } if isFirstLine && line == "" { isFirstLine = false - md.frontMatterData = append(md.frontMatterData, line) continue } isFirstLine = false diff --git a/testfiles/lorem.md.tmp b/testfiles/lorem.md.tmp deleted file mode 100644 index e6aeef6..0000000 --- a/testfiles/lorem.md.tmp +++ /dev/null @@ -1,32 +0,0 @@ -# Lorem Ipsum - -New Line -Lorem ipsum dolor sit amet, consectetur adipiscing elit. - -## Etiam - -Ad quisquam commodi est id dolorem. Laudantium omnis et praesentium modi earum voluptatem. Et est voluptatibus repellat. Vero qui totam vero. Quia odio ipsum maiores architecto sequi excepturi. Qui consectetur minus fuga facere voluptates excepturi et. -Explicabo corporis est eum esse et iusto voluptates. Perferendis ut est dolorum eius tempore laborum beatae. Cumque porro asperiores odio excepturi labore. Beatae architecto assumenda doloribus ratione sit. Quisquam eligendi officia quasi dolorem quos. -Impedit repellat iusto voluptates. Ex et placeat nesciunt doloribus est velit. Perferendis cumque vitae dolorum ullam. -Consequatur eos explicabo blanditiis necessitatibus provident. Dolores rerum modi ipsam voluptatibus. Autem sequi ad amet nihil. Maxime et eius officiis quam et voluptatem. Non vel et et quia tempora deleniti sit. Molestias repellendus earum sint dolores. -Ducimus illo dolores dolor repellendus eos. Est reiciendis sequi vero enim aut minima illum et. Nobis nihil id quas corrupti. - -### Quisquam - -Quisquam eligendi officia quasi dolorem quos. Impedit repellat iusto voluptates. Ex et placeat nesciunt doloribus est velit. Perferendis cumque vitae dolorum ullam. - -- First item -- Second item - -- [] Not a task -- [ ] First task -- [x] Completed task -- [-] Cancelled task - -1. First item -2. Second item -3. Third item - -# Blank line section - -# Last empty section diff --git a/write.go b/write.go index 4b6b930..d74dcf6 100644 --- a/write.go +++ b/write.go @@ -4,6 +4,8 @@ import ( "os" "path/filepath" "strings" + + "gopkg.in/yaml.v3" ) func (md *MarkdownFile) AddSection(line string) { @@ -57,12 +59,14 @@ func (md *MarkdownFile) Write(str ...string) (err error) { // Write the file - if len(md.frontMatterData) > 0 { - for _, line := range md.frontMatterData { - _, err = file.WriteString(line + "\n") - if err != nil { - return err - } + if md.FrontMatter != nil { + d, err := yaml.Marshal(md.FrontMatter) + if err != nil { + return err + } + _, err = file.WriteString("---\n" + string(d) + "---\n\n") + if err != nil { + return err } }