From 00b3f4ed336500a407ea45c433219d5194288c7b Mon Sep 17 00:00:00 2001 From: Alan Morgan Date: Sat, 20 Jun 2020 13:25:51 -0600 Subject: [PATCH 1/5] GH-4 Refactored facts to be 2-D array. This is a precursor to adding sub cards. This change allows for multiple ways to add cards. --- core/card.go | 86 ++++++++++++++++++++++++++++++++----------- core/core_test.go | 36 ++++++------------ core/deck.go | 7 ++-- core/filters.go | 4 +- file/edit.go | 2 +- file/file_test.go | 2 +- file/txtfile.go | 19 ++++------ termboxgui/helpers.go | 6 +-- termboxgui/tboxapp.go | 2 +- 9 files changed, 96 insertions(+), 68 deletions(-) diff --git a/core/card.go b/core/card.go index 9698922..672c8bf 100644 --- a/core/card.go +++ b/core/card.go @@ -2,6 +2,7 @@ package core import ( "strings" + "bufio" "fmt" "crypto/sha256" @@ -9,41 +10,56 @@ import ( // A card is a list of facts. Usually, but not limited to, Q&A format. type Card struct { - File string - Facts []string + file string + facts [][]string } -// Assumes a "cleaned" file string. -func NewCard(facts [][]string, file string) (*Card, error) { - c := Card{} - c.File = file - for _, x := range facts { - if len(x) > 0 { - c.Facts = append(c.Facts, strings.Join(x, " ")) +func NewCard(file string, sides string) (*Card, error) { + fact := []string{} + facts := [][]string{} + + scanner := bufio.NewScanner(strings.NewReader(sides)) + scanner.Split(bufio.ScanWords) + for scanner.Scan() { + t := scanner.Text() + if t == "@" { + facts = append(facts, fact) + fact = []string{} + } else { + fact = append(fact, t) } - } + } + + if len(fact) > 0 { + facts = append(facts, fact) + } - if len(c.Facts) > 0 { - return &c, nil + if len(facts) > 0 { + return &Card{file, facts}, nil } else { return nil, fmt.Errorf("Question not provided.") } } -func (c *Card) HasAnswer() bool { - return len(c.Facts) > 1 +// Assumes the card has a question. +/* +func (c *Card) GetSubCards() []Cards { + sub_cards := []Cards{} + question := c.GetQuestion() + for i := 1; i < len(c.facts); i++ { + [][]string{ + sc := NewCard(c.file) + sub_cards = append(sub_cards, + } } +*/ -func (c *Card) GetQuestion() string { - if len(c.Facts) > 0 { - return c.Facts[0] - } else { - return "" - } +func (c *Card) HasAnswer() bool { + return len(c.facts) > 1 } func (c *Card) String() string { - return strings.Join(c.Facts, " @ ") + return strings.Join(c.GetFacts(), " @ ") } func (c *Card) Hash() [sha256.Size]byte { @@ -53,3 +69,31 @@ func (c *Card) Hash() [sha256.Size]byte { func (c *Card) HashStr() string { return fmt.Sprintf("%x", c.Hash())[:32] } + +func (c *Card) Len() int { + return len(c.facts) +} + +func (c *Card) GetFact(i int) string { + if len(c.facts) > i { + return strings.Join(c.facts[i], " ") + } else { + return "" + } +} + +func (c *Card) GetQuestion() string { + return c.GetFact(0) +} + +func (c *Card) GetFacts() []string { + facts := []string{} + for i, _ := range c.facts { + facts = append(facts, c.GetFact(i)) + } + return facts +} + +func (c *Card) GetFile() string { + return c.file +} diff --git a/core/core_test.go b/core/core_test.go index c31f91b..a55b96b 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -7,24 +7,10 @@ import "fmt" const fullSum = "c6cd355e32654cb4ba506b529ff32288971420ead2e36fdc69e802e9e7510315" const halfSum = "c6cd355e32654cb4ba506b529ff32288" -var f1 = [][]string{ - {"hello", "there"}, - {"i'm", "a", "beard"}, -} - -var f2 = [][]string{ - {"hello"}, -} - -var f3 = [][]string{ - {"i'm", "um"}, - {"hello"}, -} - -var f4 = [][]string{ - {"alan", "the", "great"}, - {"sy", "shoe", "yu"}, -} +var f1 = []string{ "hello", "there", "@", "i'm", "a", "beard", } +var f2 = []string{ "hello", } +var f3 = []string{ "i'm", "um", "@", "hello", } +var f4 = []string{ "alan", "the", "great", "@", "sy", "shoe", "yu", } func TestMeta(t *testing.T) { a := NewMeta("2020-01-01T00:00:00Z", "0", "sm2", []string{"2.5"}) @@ -57,7 +43,7 @@ func TestCard(t *testing.T) { func TestDeck(t *testing.T) { d := NewDeck() - d.AddFacts(f1, "afile") + d.AddCardFromSides("afile", f1) if d.GetCard(0).GetQuestion() != "hello there" { t.Fail() } if !d.GetCard(0).HasAnswer() { t.Fail() } if d.GetMeta(0) != nil { t.Fail() } @@ -73,9 +59,9 @@ func TestDeck(t *testing.T) { d.Forget(0) if d.GetMeta(0) != nil { t.Fail() } if !d.GetCard(0).HasAnswer() { t.Fail() } - d.AddFacts(f1, "nofile") + d.AddCardFromSides("nofile", f1) if d.Len() != 1 { t.Fail() } - if d.GetCard(0).File != "afile" { t.Fail() } + if d.GetCard(0).GetFile() != "afile" { t.Fail() } d.FilterOutFile("nofile") if d.Len() != 1 { t.Fail() } d.FilterOutFile("afile") @@ -86,10 +72,10 @@ func TestDeck(t *testing.T) { func TestDeckMove(t *testing.T) { d := NewDeck() - d.AddFacts(f1, "afile") - d.AddFacts(f2, "afile") - d.AddFacts(f3, "afile") - d.AddFacts(f4, "afile") + d.AddCardFromSides("afile", f1) + d.AddCardFromSides("afile", f2) + d.AddCardFromSides("afile", f3) + d.AddCardFromSides("afile", f4) d.TopToEnd() if d.TopCard().GetQuestion() != "hello" { panic("Bad moves") } if d.Len() != 4 { panic("Bad len") } diff --git a/core/deck.go b/core/deck.go index e693ac9..66f9058 100644 --- a/core/deck.go +++ b/core/deck.go @@ -71,11 +71,12 @@ func (d *Deck) InsertCard(c *Card, i int) error { } } -func (d *Deck) AddFacts(facts [][]string, file string) error { - if c, err := NewCard(facts, file); err == nil { +func (d *Deck) AddCardFromSides(file string, sides string) error { + if c, err := NewCard(file, sides); err == nil { return d.AddCard(c) + } else { + return err } - return nil } func (d *Deck) AddMeta(h string, m *Meta) { diff --git a/core/filters.go b/core/filters.go index ab52426..6ae37fe 100644 --- a/core/filters.go +++ b/core/filters.go @@ -22,7 +22,7 @@ func (d *Deck) FilterNumber(param int) { func (d *Deck) FileIntersection(path string, other_deck *Deck) { d.filter(func(i int) bool { _, contains := other_deck.Cmap[d.refs[i]] - return d.GetCard(i).File == path && !contains + return d.GetCard(i).GetFile() == path && !contains }) } @@ -35,7 +35,7 @@ func (d *Deck) OuterLeftJoin(other_deck *Deck) { func (d *Deck) FilterOutFile(path string) { d.filter(func(i int) bool { - return d.GetCard(i).File == path + return d.GetCard(i).GetFile() == path }) } diff --git a/file/edit.go b/file/edit.go index da4559b..0a58550 100644 --- a/file/edit.go +++ b/file/edit.go @@ -16,7 +16,7 @@ func EditFile(d *core.Deck, cfg *Config) error { // We need to get information for the top card first. cur_hash, cur_card, cur_meta := d.Top() - file_name := cur_card.File + file_name := cur_card.GetFile() // Save the contents of the file now. deck_before := core.NewDeck() diff --git a/file/file_test.go b/file/file_test.go index a039dd3..649e1a3 100644 --- a/file/file_test.go +++ b/file/file_test.go @@ -40,7 +40,7 @@ func TestReadCardsToDeck(t *testing.T) { _, c, _ := d.Get(i) switch i { case 0: if c.GetQuestion() != "hi" { t.Fail() } - if c.File != "nihao" { t.Fail() } + if c.GetFile() != "nihao" { t.Fail() } case 1: if c.GetQuestion() != "yoyo man go" { t.Fail() } } } diff --git a/file/txtfile.go b/file/txtfile.go index 05688bf..7a50fb3 100644 --- a/file/txtfile.go +++ b/file/txtfile.go @@ -2,6 +2,7 @@ package file import ( "bufio" + "strings" "io" "fmt" "os" @@ -43,7 +44,7 @@ func ReadCardsToDeck(d *core.Deck, filename string) error { func ReadCardsToDeckHelper(r io.Reader, d *core.Deck, f string) { // Initialization. - facts := [][]string{} + facts := []string{} state := false var td *core.Deck @@ -55,27 +56,23 @@ func ReadCardsToDeckHelper(r io.Reader, d *core.Deck, f string) { t := scanner.Text() if state { - if t == "@" { - facts = append(facts, []string{}) - } else if t == "@>" { - td.AddFacts(facts, f) - facts = [][]string{{}} + if t == "@>" { + td.AddCardFromSides(f, strings.Join(facts, " ")) + facts = []string{} } else if t == "<@" { - td.AddFacts(facts, f) + td.AddCardFromSides(f, strings.Join(facts, " ")) for i := 0; i < td.Len(); i++ { d.AddCard(td.GetCard(i)) } state = false } else { - if i := len(facts)-1; i >= 0 { - facts[i] = append(facts[i], t) - } + facts = append(facts, t) } } else if t == "@>" { // create td td = core.NewDeck() state = true - facts = [][]string{{}} + facts = []string{} } } diff --git a/termboxgui/helpers.go b/termboxgui/helpers.go index 21a86fa..d2fb9e3 100644 --- a/termboxgui/helpers.go +++ b/termboxgui/helpers.go @@ -100,10 +100,10 @@ func tbvertical(x int, color termbox.Attribute) { func tbprint_card(c *core.Card, amount int) { y := 0 - for i := 0; i < len(c.Facts) && i < amount; i++ { + for i := 0; i < c.Len() && i < amount; i++ { color := termbox.ColorCyan if i > 0 { color = termbox.ColorWhite } - _, y = tbprintwrap(0, y, color, coldef, c.Facts[i]) + _, y = tbprintwrap(0, y, color, coldef, c.GetFact(i)) y++ } } @@ -112,7 +112,7 @@ func tbprint_statusbar(d *core.Deck) { _, h := termbox.Size() color := termbox.ColorBlue tbhorizontal(h-2, color) - msg := fmt.Sprintf("%d concards - %s", d.Len(), d.TopCard().File) + msg := fmt.Sprintf("%d concards - %s", d.Len(), d.TopCard().GetFile()) tbprint(0, h-2, termbox.ColorWhite|termbox.AttrBold, color, msg) } diff --git a/termboxgui/tboxapp.go b/termboxgui/tboxapp.go index 7e8fc70..4e9ec4b 100644 --- a/termboxgui/tboxapp.go +++ b/termboxgui/tboxapp.go @@ -111,7 +111,7 @@ func TermBoxRun(d *core.Deck, cfg *file.Config) error { } } else if inp == " " || inp == "\r" { card_shown++ - if l := len(d.GetCard(0).Facts); card_shown > l { + if l := d.GetCard(0).Len(); card_shown > l { card_shown = 1 } } From af08272ba98533eca3ea6b424d5ec10c3ffb49a7 Mon Sep 17 00:00:00 2001 From: Alan Morgan Date: Sat, 20 Jun 2020 13:54:25 -0600 Subject: [PATCH 2/5] GH-4 It works. No tests though. --- core/card.go | 21 +++++++++++---------- core/deck.go | 16 +++++++++++----- file/txtfile.go | 4 ++-- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/core/card.go b/core/card.go index 672c8bf..66afd17 100644 --- a/core/card.go +++ b/core/card.go @@ -41,18 +41,19 @@ func NewCard(file string, sides string) (*Card, error) { } } -// Assumes the card has a question. -/* -func (c *Card) GetSubCards() []Cards { - sub_cards := []Cards{} - question := c.GetQuestion() - for i := 1; i < len(c.facts); i++ { - [][]string{ - sc := NewCard(c.file) - sub_cards = append(sub_cards, +func (c *Card) GetSubCards() []*Card { + sub_cards := []*Card{} + question := c.GetQuestion() + answers := c.GetFacts()[1:] + for _, answer := range answers { + if sc, err := NewCard(c.file, answer + " @ " + question); err == nil { + sub_cards = append(sub_cards, sc) + } else { + panic("Error: Sub card was not created due to bad parent card. This is a logic error and should be fixed.") + } } + return sub_cards } -*/ func (c *Card) HasAnswer() bool { return len(c.facts) > 1 diff --git a/core/deck.go b/core/deck.go index 66f9058..90f1bbd 100644 --- a/core/deck.go +++ b/core/deck.go @@ -54,7 +54,6 @@ func (d *Deck) AddCard(c *Card) error { } } -// TODO: Error handling here. func (d *Deck) InsertCard(c *Card, i int) error { hash := c.HashStr() _, exists := d.Cmap[hash] @@ -71,12 +70,19 @@ func (d *Deck) InsertCard(c *Card, i int) error { } } -func (d *Deck) AddCardFromSides(file string, sides string) error { - if c, err := NewCard(file, sides); err == nil { - return d.AddCard(c) +func (d *Deck) AddCardAndSubCardsFromSides(file string, sides string) []error { + errors := []error{} + if c, create_err := NewCard(file, sides); create_err == nil { + cards := append(c.GetSubCards(), c) + for _, c := range cards { + if add_err := d.AddCard(c); add_err != nil { + errors = append(errors, add_err) + } + } } else { - return err + errors = append(errors, create_err) } + return errors } func (d *Deck) AddMeta(h string, m *Meta) { diff --git a/file/txtfile.go b/file/txtfile.go index 7a50fb3..7981735 100644 --- a/file/txtfile.go +++ b/file/txtfile.go @@ -57,10 +57,10 @@ func ReadCardsToDeckHelper(r io.Reader, d *core.Deck, f string) { if state { if t == "@>" { - td.AddCardFromSides(f, strings.Join(facts, " ")) + td.AddCardAndSubCardsFromSides(f, strings.Join(facts, " ")) facts = []string{} } else if t == "<@" { - td.AddCardFromSides(f, strings.Join(facts, " ")) + td.AddCardAndSubCardsFromSides(f, strings.Join(facts, " ")) for i := 0; i < td.Len(); i++ { d.AddCard(td.GetCard(i)) } From 1434e520d63c7e702e38cbdab87e145d578d1798 Mon Sep 17 00:00:00 2001 From: Alan Morgan Date: Sun, 21 Jun 2020 10:17:46 -0600 Subject: [PATCH 3/5] Edit cards more modular & Argparse library change. Yep, that's it. It will be easier to test the Edit functionality now. I also made my version of Argparse backwards compatible, so that's cool. --- README.md | 15 ++--------- core/card.go | 8 +++--- file/argparse.go | 10 +++++--- file/edit.go | 59 ++++++++++++++++++++++++++++++------------- termboxgui/helpers.go | 2 +- termboxgui/tboxapp.go | 2 +- 6 files changed, 57 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index a62346d..9d48b39 100644 --- a/README.md +++ b/README.md @@ -56,22 +56,11 @@ concards README.md ``` ## All Special Tokens -All the special tokens that are a part of concards syntax are below. Just add -"@" signs to escape them! +All the special tokens that are a part of concards syntax are below. ``` @> = Starts a concards block. Starts a question. <@ = Ends the concards block. @ = Separates answers. - -@@> = "@>" -<@@ = "<@" -@@ = "@" - -@@@> = "@@>" -<@@@ = "<@@" -@@@ = "@@" - -... ``` ## Advanced Usage @@ -85,7 +74,7 @@ Here is an example meta-data file: Here is the same file, but annotated: ``` -sha256sum | review timestamp | streak | alg | data +sha256sum cut in half | review timestamp | streak | alg | data ---------------------------------+-----------------------+--------+-----+----- 3dda75cb44ed447186834541475f32e2 | 2019-01-01T00:00:00Z | 0 | sm2 | 2.5 8525b45f883c05eec46b4f7a88e7f7ef | 2020-01-01T00:00:00Z | 0 | sm2 | 2.5 diff --git a/core/card.go b/core/card.go index 66afd17..3393d01 100644 --- a/core/card.go +++ b/core/card.go @@ -23,9 +23,11 @@ func NewCard(file string, sides string) (*Card, error) { for scanner.Scan() { t := scanner.Text() if t == "@" { - facts = append(facts, fact) - fact = []string{} - } else { + if len(fact) > 0 { + facts = append(facts, fact) + fact = []string{} + } + } else if len(t) > 0 { fact = append(fact, t) } } diff --git a/file/argparse.go b/file/argparse.go index adc2c27..a6627ce 100644 --- a/file/argparse.go +++ b/file/argparse.go @@ -62,7 +62,7 @@ func GenConfig() *Config { f_memorize := parser.Flag("m", "memorize", &argparse.Options{Help: "Show cards available to be memorized"}) f_done := parser.Flag("d", "done", &argparse.Options{Help: "Show cards not available to be reviewed or memorized"}) f_print := parser.Flag("p", "print", &argparse.Options{Help: "Prints all cards, one line per card"}) - f_number := parser.Int("n", "number", &argparse.Options{Default: 0, Help: "Limit the number of cards in the program to \"#\""}) + f_number := parser.Int("n", "number", &argparse.Options{Default: 0, Help: "How many cards to review"}) f_editor := parser.String("E", "editor", &argparse.Options{Default: getDefaultEditor(), Help: "Which editor to use. Defaults to \"$EDITOR\""}) f_meta := parser.String("M", "meta", &argparse.Options{Default: getDefaultMeta(), Help: "Path to meta file. Defaults to \"$CONCARDS_META\" or \"~/.concards-meta\""}) @@ -71,14 +71,18 @@ func GenConfig() *Config { help += fmt.Sprintf("%s\n\nUsage:\n %s [OPTIONS]... [FILE|FOLDER]...\n\nOptions:\n", c.GetDescription(), c.GetName()) for _, arg := range c.GetArgs() { - help += fmt.Sprintf(" -%s --%-9s %s.\n", arg.GetSname(), arg.GetLname(), arg.GetOpts().Help) + if arg.IsFlag() { + help += fmt.Sprintf(" -%s --%-9s %s.\n", arg.GetSname(), arg.GetLname(), arg.GetOpts().Help) + } else { + help += fmt.Sprintf(" -%s --%-9s %s.\n", arg.GetSname(), arg.GetLname() + " " + arg.GetSname(), arg.GetOpts().Help) + } } return help } // Parse input - files, err := parser.Parse(os.Args) + files, err := parser.ParseReturnArguments(os.Args) if err != nil { fmt.Print(parser.Help(nil)) os.Exit(1) diff --git a/file/edit.go b/file/edit.go index 0a58550..00965cb 100644 --- a/file/edit.go +++ b/file/edit.go @@ -8,35 +8,58 @@ import ( "github.com/alanxoc3/concards/core" ) -// Assumes the deck is sorted how you want it to be sorted. -func EditFile(d *core.Deck, cfg *Config) error { - if d.IsEmpty() { - return fmt.Errorf("Error: The deck is empty.") - } +type DeckFunc func(string) (*core.Deck, error) - // We need to get information for the top card first. - cur_hash, cur_card, cur_meta := d.Top() - file_name := cur_card.GetFile() +func ReadCards(filename string) (*core.Deck, error) { + d := core.NewDeck() + if err := ReadCardsToDeck(d, filename); err != nil { + return nil, err + } + return d, nil +} - // Save the contents of the file now. - deck_before := core.NewDeck() - ReadCardsToDeck(deck_before, file_name) +func EditCards(filename string, cfg *Config) (*core.Deck, error) { + if cfg == nil { panic("Config was nil when passed to edit function.") } - // Then edit the file. - cmd := exec.Command(cfg.Editor, file_name) + // Load the file with your favorite editor. + cmd := exec.Command(cfg.Editor, filename) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout if err := cmd.Run(); err != nil { - return fmt.Errorf("Error: The editor returned an error code.") + return nil, fmt.Errorf("Error: The editor returned an error code.") } - // Save the contents of the file after. - deck_after := core.NewDeck() - ReadCardsToDeck(deck_after, file_name) + return ReadCards(filename) +} + + +func EditLambda(cfg *Config) DeckFunc { + return func(filename string) (*core.Deck, error) { + return EditCards(filename, cfg) + } +} + +// Assumes the deck is sorted how you want it to be sorted. +func EditFile(d *core.Deck, rf DeckFunc, ef DeckFunc) error { + if d.IsEmpty() { + return fmt.Errorf("Error: The deck is empty.") + } + + // We need to get information for the top card first. + cur_hash, cur_card, cur_meta := d.Top() + filename := cur_card.GetFile() + + // Deck before editing. + deck_before, e := rf(filename) + if e != nil { return e } + + // Deck after editing. + deck_after, e := ef(filename) + if e != nil { return e } // Take out any card that was removed from the file. - d.FileIntersection(file_name, deck_after) + d.FileIntersection(filename, deck_after) // Get only the cards that were created in the file. deck_after.OuterLeftJoin(deck_before) diff --git a/termboxgui/helpers.go b/termboxgui/helpers.go index d2fb9e3..31df211 100644 --- a/termboxgui/helpers.go +++ b/termboxgui/helpers.go @@ -112,7 +112,7 @@ func tbprint_statusbar(d *core.Deck) { _, h := termbox.Size() color := termbox.ColorBlue tbhorizontal(h-2, color) - msg := fmt.Sprintf("%d concards - %s", d.Len(), d.TopCard().GetFile()) + msg := fmt.Sprintf("%d cards - %s", d.Len(), d.TopCard().GetFile()) tbprint(0, h-2, termbox.ColorWhite|termbox.AttrBold, color, msg) } diff --git a/termboxgui/tboxapp.go b/termboxgui/tboxapp.go index 4e9ec4b..b0516d3 100644 --- a/termboxgui/tboxapp.go +++ b/termboxgui/tboxapp.go @@ -81,7 +81,7 @@ func TermBoxRun(d *core.Deck, cfg *file.Config) error { d.TopToEnd() save(d) } else if inp == "e" { - err := file.EditFile(d, cfg) + err := file.EditFile(d, file.ReadCards, file.EditLambda(cfg)) if err != nil { update_stat_msg(err.Error(), termbox.ColorRed) From f7988facea0f6730f7e220e1c49478a3043560fc Mon Sep 17 00:00:00 2001 From: Alan Morgan Date: Sat, 27 Jun 2020 00:42:25 -0600 Subject: [PATCH 4/5] GH-4 Doing sides is now optional. By default, sides are disabled. --- core/deck.go | 8 ++++++-- file/argparse.go | 19 +++++++++++-------- file/edit.go | 21 +++++++-------------- file/txtfile.go | 12 +++++++----- main.go | 2 +- termboxgui/tboxapp.go | 2 +- 6 files changed, 33 insertions(+), 31 deletions(-) diff --git a/core/deck.go b/core/deck.go index 90f1bbd..657646e 100644 --- a/core/deck.go +++ b/core/deck.go @@ -70,10 +70,14 @@ func (d *Deck) InsertCard(c *Card, i int) error { } } -func (d *Deck) AddCardAndSubCardsFromSides(file string, sides string) []error { +func (d *Deck) AddCardFromSides(file string, sides string, include_sides bool) []error { errors := []error{} if c, create_err := NewCard(file, sides); create_err == nil { - cards := append(c.GetSubCards(), c) + cards := []*Card{c} + if include_sides { + cards = append(cards, c.GetSubCards()...) + } + for _, c := range cards { if add_err := d.AddCard(c); add_err != nil { errors = append(errors, add_err) diff --git a/file/argparse.go b/file/argparse.go index a6627ce..a1923c1 100644 --- a/file/argparse.go +++ b/file/argparse.go @@ -12,6 +12,7 @@ type Config struct { IsReview bool IsMemorize bool IsDone bool + IsSides bool IsPrint bool IsStream bool @@ -57,14 +58,15 @@ func GenConfig() *Config { parser := argparse.NewParser("concards", "Concards is a simple CLI based SRS flashcard app.") // Create flags - f_version := parser.Flag("V", "version", &argparse.Options{Help: "Concards build information."}) - f_review := parser.Flag("r", "review", &argparse.Options{Help: "Show cards available to be reviewed"}) - f_memorize := parser.Flag("m", "memorize", &argparse.Options{Help: "Show cards available to be memorized"}) - f_done := parser.Flag("d", "done", &argparse.Options{Help: "Show cards not available to be reviewed or memorized"}) - f_print := parser.Flag("p", "print", &argparse.Options{Help: "Prints all cards, one line per card"}) - f_number := parser.Int("n", "number", &argparse.Options{Default: 0, Help: "How many cards to review"}) - f_editor := parser.String("E", "editor", &argparse.Options{Default: getDefaultEditor(), Help: "Which editor to use. Defaults to \"$EDITOR\""}) - f_meta := parser.String("M", "meta", &argparse.Options{Default: getDefaultMeta(), Help: "Path to meta file. Defaults to \"$CONCARDS_META\" or \"~/.concards-meta\""}) + f_version := parser.Flag("V", "version", &argparse.Options{Help: "Concards build information."}) + f_review := parser.Flag("r", "review", &argparse.Options{Help: "Show cards available to be reviewed."}) + f_memorize := parser.Flag("m", "memorize", &argparse.Options{Help: "Show cards available to be memorized."}) + f_done := parser.Flag("d", "done", &argparse.Options{Help: "Show cards not available to be reviewed or memorized."}) + f_sides := parser.Flag("s", "sides", &argparse.Options{Help: "Add cards for all sides."}) + f_print := parser.Flag("p", "print", &argparse.Options{Help: "Prints all cards, one line per card."}) + f_number := parser.Int("n", "number", &argparse.Options{Default: 0, Help: "How many cards to review."}) + f_editor := parser.String("E", "editor", &argparse.Options{Default: getDefaultEditor(), Help: "Which editor to use. Defaults to \"$EDITOR\""}) + f_meta := parser.String("M", "meta", &argparse.Options{Default: getDefaultMeta(), Help: "Path to meta file. Defaults to \"$CONCARDS_META\" or \"~/.concards-meta\""}) parser.HelpFunc = func(c *argparse.Command, msg interface{}) string { var help string @@ -98,6 +100,7 @@ func GenConfig() *Config { c.IsReview = *f_review c.IsMemorize = *f_memorize c.IsDone = *f_done + c.IsSides = *f_sides c.IsPrint = *f_print c.IsStream = false diff --git a/file/edit.go b/file/edit.go index 00965cb..f7bd952 100644 --- a/file/edit.go +++ b/file/edit.go @@ -8,11 +8,11 @@ import ( "github.com/alanxoc3/concards/core" ) -type DeckFunc func(string) (*core.Deck, error) +type DeckFunc func(string, *Config) (*core.Deck, error) -func ReadCards(filename string) (*core.Deck, error) { +func ReadCards(filename string, cfg *Config) (*core.Deck, error) { d := core.NewDeck() - if err := ReadCardsToDeck(d, filename); err != nil { + if err := ReadCardsToDeck(d, filename, cfg.IsSides); err != nil { return nil, err } return d, nil @@ -30,18 +30,11 @@ func EditCards(filename string, cfg *Config) (*core.Deck, error) { return nil, fmt.Errorf("Error: The editor returned an error code.") } - return ReadCards(filename) -} - - -func EditLambda(cfg *Config) DeckFunc { - return func(filename string) (*core.Deck, error) { - return EditCards(filename, cfg) - } + return ReadCards(filename, cfg) } // Assumes the deck is sorted how you want it to be sorted. -func EditFile(d *core.Deck, rf DeckFunc, ef DeckFunc) error { +func EditFile(d *core.Deck, cfg *Config, rf DeckFunc, ef DeckFunc) error { if d.IsEmpty() { return fmt.Errorf("Error: The deck is empty.") } @@ -51,11 +44,11 @@ func EditFile(d *core.Deck, rf DeckFunc, ef DeckFunc) error { filename := cur_card.GetFile() // Deck before editing. - deck_before, e := rf(filename) + deck_before, e := rf(filename, cfg) if e != nil { return e } // Deck after editing. - deck_after, e := ef(filename) + deck_after, e := ef(filename, cfg) if e != nil { return e } // Take out any card that was removed from the file. diff --git a/file/txtfile.go b/file/txtfile.go index 7981735..e7dae29 100644 --- a/file/txtfile.go +++ b/file/txtfile.go @@ -12,7 +12,7 @@ import ( ) // Open opens filename and loads cards into new deck -func ReadCardsToDeck(d *core.Deck, filename string) error { +func ReadCardsToDeck(d *core.Deck, filename string, include_sides bool) error { err := filepath.Walk(filename, func(path string, info os.FileInfo, e error) error { if e != nil { return e @@ -33,7 +33,7 @@ func ReadCardsToDeck(d *core.Deck, filename string) error { return fmt.Errorf("Error: Unable to open file \"%s\"", filename) } else { defer f.Close() - ReadCardsToDeckHelper(f, d, abs_path) + ReadCardsToDeckHelper(f, d, abs_path, include_sides) } return nil @@ -42,7 +42,7 @@ func ReadCardsToDeck(d *core.Deck, filename string) error { return err } -func ReadCardsToDeckHelper(r io.Reader, d *core.Deck, f string) { +func ReadCardsToDeckHelper(r io.Reader, d *core.Deck, f string, include_sides bool) { // Initialization. facts := []string{} state := false @@ -57,10 +57,12 @@ func ReadCardsToDeckHelper(r io.Reader, d *core.Deck, f string) { if state { if t == "@>" { - td.AddCardAndSubCardsFromSides(f, strings.Join(facts, " ")) + td.AddCardFromSides(f, strings.Join(facts, " "), include_sides) + facts = []string{} } else if t == "<@" { - td.AddCardAndSubCardsFromSides(f, strings.Join(facts, " ")) + td.AddCardFromSides(f, strings.Join(facts, " "), include_sides) + for i := 0; i < td.Len(); i++ { d.AddCard(td.GetCard(i)) } diff --git a/main.go b/main.go index 20faa05..9fb6671 100644 --- a/main.go +++ b/main.go @@ -23,7 +23,7 @@ func main() { } for _, f := range c.Files { - if err := file.ReadCardsToDeck(d, f); err != nil { + if err := file.ReadCardsToDeck(d, f, c.IsSides); err != nil { fmt.Printf("Error: File \"%s\" does not exist!\n", f) os.Exit(1) } diff --git a/termboxgui/tboxapp.go b/termboxgui/tboxapp.go index b0516d3..29d0ccc 100644 --- a/termboxgui/tboxapp.go +++ b/termboxgui/tboxapp.go @@ -81,7 +81,7 @@ func TermBoxRun(d *core.Deck, cfg *file.Config) error { d.TopToEnd() save(d) } else if inp == "e" { - err := file.EditFile(d, file.ReadCards, file.EditLambda(cfg)) + err := file.EditFile(d, cfg, file.ReadCards, file.EditCards) if err != nil { update_stat_msg(err.Error(), termbox.ColorRed) From c0dc69ba7957f9d59381a18da80bc090de2a6c9f Mon Sep 17 00:00:00 2001 From: Alan Morgan Date: Sat, 27 Jun 2020 01:06:34 -0600 Subject: [PATCH 5/5] GH-4 Updated the tests for multi sides. --- core/core_test.go | 47 ++++++++++++++++++++++++++++++++++++----------- file/file_test.go | 4 ++-- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/core/core_test.go b/core/core_test.go index a55b96b..082be25 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -7,10 +7,11 @@ import "fmt" const fullSum = "c6cd355e32654cb4ba506b529ff32288971420ead2e36fdc69e802e9e7510315" const halfSum = "c6cd355e32654cb4ba506b529ff32288" -var f1 = []string{ "hello", "there", "@", "i'm", "a", "beard", } -var f2 = []string{ "hello", } -var f3 = []string{ "i'm", "um", "@", "hello", } -var f4 = []string{ "alan", "the", "great", "@", "sy", "shoe", "yu", } +var f1 = "hello there @ i'm a beard" +var f2 = "hello" +var f3 = "i'm um @ hello" +var f4 = "alan the great @ sy shoe yu" +var f5 = "a @ b @ c @ d e @ f @ @ g" func TestMeta(t *testing.T) { a := NewMeta("2020-01-01T00:00:00Z", "0", "sm2", []string{"2.5"}) @@ -28,7 +29,7 @@ func TestParse(t *testing.T) { } func TestCard(t *testing.T) { - c, err := NewCard(f1, "") + c, err := NewCard("", f1) if err != nil { t.FailNow() } txt := c.String() @@ -43,7 +44,7 @@ func TestCard(t *testing.T) { func TestDeck(t *testing.T) { d := NewDeck() - d.AddCardFromSides("afile", f1) + d.AddCardFromSides("afile", f1, false) if d.GetCard(0).GetQuestion() != "hello there" { t.Fail() } if !d.GetCard(0).HasAnswer() { t.Fail() } if d.GetMeta(0) != nil { t.Fail() } @@ -59,7 +60,7 @@ func TestDeck(t *testing.T) { d.Forget(0) if d.GetMeta(0) != nil { t.Fail() } if !d.GetCard(0).HasAnswer() { t.Fail() } - d.AddCardFromSides("nofile", f1) + d.AddCardFromSides("nofile", f1, false) if d.Len() != 1 { t.Fail() } if d.GetCard(0).GetFile() != "afile" { t.Fail() } d.FilterOutFile("nofile") @@ -72,10 +73,10 @@ func TestDeck(t *testing.T) { func TestDeckMove(t *testing.T) { d := NewDeck() - d.AddCardFromSides("afile", f1) - d.AddCardFromSides("afile", f2) - d.AddCardFromSides("afile", f3) - d.AddCardFromSides("afile", f4) + d.AddCardFromSides("afile", f1, false) + d.AddCardFromSides("afile", f2, false) + d.AddCardFromSides("afile", f3, false) + d.AddCardFromSides("afile", f4, false) d.TopToEnd() if d.TopCard().GetQuestion() != "hello" { panic("Bad moves") } if d.Len() != 4 { panic("Bad len") } @@ -90,3 +91,27 @@ func TestDeckMove(t *testing.T) { d.TopToEnd() if d.TopCard().GetQuestion() != "hello there" { panic("Bad moves") } } + +func TestAddSubCards(t *testing.T) { + d := NewDeck() + d.AddCardFromSides("dat-file", f5, true) + + if d.TopCard().GetQuestion() != "a" { panic("Subcards were inserted before the parent card.") } + if d.Len() != 6 { panic("Wrong number of sub cards inserted.") } + d.DelTop() + if d.TopCard().GetQuestion() != "b" { panic("Second card should be the first sub card.") } + if d.TopCard().GetFact(1) != "a" { panic("Answer isn't the parent card.") } + if d.Len() != 5 { panic("Delete didn't work.") } + + if d.GetCard(1).GetQuestion() != "c" { panic("Sub cards not inserted in the correct order.") } + if d.GetCard(1).GetFact(1) != "a" { panic("Sub card doesn't have parent as the answer.") } + + if d.GetCard(2).GetQuestion() != "d e" { panic("Sub cards not inserted in the correct order.") } + if d.GetCard(2).GetFact(1) != "a" { panic("Sub card doesn't have parent as the answer.") } + + if d.GetCard(3).GetQuestion() != "f" { panic("Sub cards not inserted in the correct order.") } + if d.GetCard(3).GetFact(1) != "a" { panic("Sub card doesn't have parent as the answer.") } + + if d.GetCard(4).GetQuestion() != "g" { panic("Sub cards not inserted in the correct order.") } + if d.GetCard(4).GetFact(1) != "a" { panic("Sub card doesn't have parent as the answer.") } +} diff --git a/file/file_test.go b/file/file_test.go index 649e1a3..49cf23c 100644 --- a/file/file_test.go +++ b/file/file_test.go @@ -17,7 +17,7 @@ b718c81a83d82bb83f82b0a8b18bb82b 2020-01-11T00:00:00Z 27 sm2 .05 func TestReadMetasToDeck(t *testing.T) { d := core.NewDeck() - ReadCardsToDeckHelper(strings.NewReader(f1 + f2), d, "") + ReadCardsToDeckHelper(strings.NewReader(f1 + f2), d, "", false) ReadMetasToDeckHelper(strings.NewReader(c1), d) for i := 0; i < d.Len(); i++ { @@ -34,7 +34,7 @@ func TestReadMetasToDeck(t *testing.T) { func TestReadCardsToDeck(t *testing.T) { d := core.NewDeck() - ReadCardsToDeckHelper(strings.NewReader(f2), d, "nihao") + ReadCardsToDeckHelper(strings.NewReader(f2), d, "nihao", false) for i := 0; i < d.Len(); i++ { _, c, _ := d.Get(i)