diff --git a/internal/model/model.go b/internal/model/model.go index 8087f4a..acfb9e0 100644 --- a/internal/model/model.go +++ b/internal/model/model.go @@ -4,10 +4,12 @@ import ( "bufio" "errors" "fmt" + "github.com/maaslalani/slides/internal/file" + "github.com/maaslalani/slides/internal/navigation" + "github.com/maaslalani/slides/internal/process" "io" "io/ioutil" "os" - "strconv" "strings" "time" @@ -15,9 +17,7 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/glamour" "github.com/maaslalani/slides/internal/code" - "github.com/maaslalani/slides/internal/file" "github.com/maaslalani/slides/internal/meta" - "github.com/maaslalani/slides/internal/process" "github.com/maaslalani/slides/styles" ) @@ -42,8 +42,6 @@ type Model struct { type fileWatchMsg struct{} -type repeatableFunction func() int - var fileInfo os.FileInfo func (m Model) Init() tea.Cmd { @@ -104,38 +102,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyMsg: keyPress := msg.String() + + shouldClearVirtualText := false + switch keyPress { - case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9": - if m.bufferIsNumeric() { - m.buffer += keyPress - } else { - m.buffer = keyPress - } - case "ctrl+c", "q": - return m, tea.Quit - case "g": - switch m.buffer { - case "g": - m.Page = m.navigateFirst() - m.buffer = "" - default: - m.buffer = "g" - } - case "G": - if m.bufferIsNumeric() { - m.Page = m.navigatePage() - } else { - m.Page = m.navigateLast() - } - m.buffer = "" - case " ", "down", "j", "right", "l", "enter", "n": - m.Page = m.navigateNext() - m.VirtualText = "" - m.buffer = "" - case "up", "k", "left", "h", "p": - m.Page = m.navigatePrevious() - m.VirtualText = "" - m.buffer = "" case "ctrl+e": // Run code blocks blocks, err := code.Parse(m.Slides[m.Page]) @@ -150,7 +120,14 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { outs = append(outs, res.Out) } m.VirtualText = strings.Join(outs, "\n") - m.buffer = "" + case "ctrl+c", "q": + return m, tea.Quit + default: + m.buffer, m.Page, shouldClearVirtualText = navigation.Navigate(m.buffer, keyPress, m.Page, len(m.Slides)) + } + + if shouldClearVirtualText { + m.VirtualText = "" } case fileWatchMsg: @@ -194,54 +171,6 @@ func (m *Model) paging() string { } } -func (m Model) bufferIsNumeric() bool { - _, err := strconv.Atoi(m.buffer) - return err == nil -} - -func (m Model) navigateFirst() int { - return 0 -} - -func (m Model) navigateNext() int { - return m.repeatableAction(func() int { - if m.Page < len(m.Slides)-1 { - m.Page++ - } - - return m.Page - }) -} - -func (m Model) navigatePage() int { - page, _ := strconv.Atoi(m.buffer) - page -= 1 - - if page > len(m.Slides) -1 { - return len(m.Slides) - 1 - } - - if page < 0 { - return 0 - } - - return page -} - -func (m Model) navigatePrevious() int { - return m.repeatableAction(func() int { - if m.Page > 0 { - m.Page-- - } - - return m.Page - }) -} - -func (m Model) navigateLast() int { - return len(m.Slides) - 1 -} - func readFile(path string) (string, error) { s, err := os.Stat(path) if err != nil { @@ -296,22 +225,3 @@ func readStdin() (string, error) { return b.String(), nil } - -func (m Model) repeatableAction(fn repeatableFunction) int { - if !m.bufferIsNumeric() { - return fn() - } - - repeat, _ := strconv.Atoi(m.buffer) - - if repeat == 0 { - // This is how behaviour works in Vim, so following principle of least astonishment. - return fn() - } - - for i := 0; i < repeat; i++ { - m.Page = fn() - } - - return m.Page -} diff --git a/internal/navigation/navigation.go b/internal/navigation/navigation.go new file mode 100644 index 0000000..8fb33a3 --- /dev/null +++ b/internal/navigation/navigation.go @@ -0,0 +1,110 @@ +package navigation + +import ( + "strconv" +) + +type repeatableFunction func(slide, totalSlides int) int + +// Navigate receives the current buffer, keyPress, current slide, and total number of slides. +// Navigate returns the new (updated) buffer, new current slide, and true if virtual text should be cleared. +// For example, if showing user slide 1 and there are 10 slides available, slide will be 0 and numSlides will be 10. +func Navigate(buffer, keyPress string, slide, numSlides int) (string, int, bool) { + // Implementation + + switch keyPress { + case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9": + if bufferIsNumeric(buffer) { + return buffer + keyPress, slide, false + } else { + return keyPress, slide, false + } + case "g": + switch buffer { + case "g": + return "", navigateFirst(), false + default: + return "g", slide, false + } + case "G": + if bufferIsNumeric(buffer) { + return "", navigateSlide(buffer, numSlides), false + } else { + return "", navigateLast(numSlides), false + } + case " ", "down", "j", "right", "l", "enter", "n": + return "", navigateNext(buffer, slide, numSlides), true + case "up", "k", "left", "h", "p": + return "", navigatePrevious(buffer, slide, numSlides), true + default: + return "", slide, false + } +} + +func bufferIsNumeric(buffer string) bool { + _, err := strconv.Atoi(buffer) + return err == nil +} + +func navigateFirst() int { + return 0 +} + +func navigateNext(buffer string, slide, numSlides int) int { + return repeatableAction(func(slide, totalSlides int) int { + if slide < totalSlides-1 { + return slide + 1 + } + + return totalSlides - 1 + }, buffer, slide, numSlides) +} + +func navigateSlide(buffer string, numSlides int) int { + destinationSlide, _ := strconv.Atoi(buffer) + destinationSlide -= 1 + + if destinationSlide > numSlides -1 { + return numSlides - 1 + } + + if destinationSlide < 0 { + return 0 + } + + return destinationSlide +} + +func navigatePrevious(buffer string, slide, totalSlides int) int { + return repeatableAction(func(slide, totalSlides int) int { + if slide > 0 { + return slide - 1 + } + + return slide + }, buffer, slide, totalSlides) +} + +func navigateLast(numSlides int) int { + return numSlides - 1 +} + +func repeatableAction(fn repeatableFunction, buffer string, slide, totalSlides int) int { + if !bufferIsNumeric(buffer) { + return fn(slide, totalSlides) + } + + repeat, _ := strconv.Atoi(buffer) + currentSlide := slide + + if repeat == 0 { + // This is how behaviour works in Vim, so following principle of least astonishment. + return fn(slide, totalSlides) + } + + for i := 0; i < repeat; i++ { + currentSlide = fn(currentSlide, totalSlides) + } + + return currentSlide +}