package main import ( "math" ) func (m *model) AtTop() bool { return m.offset <= 0 } func (m *model) AtBottom() bool { return m.offset >= m.maxYOffset() } func (m *model) PastBottom() bool { return m.offset > m.maxYOffset() } func (m *model) ScrollPercent() float64 { if m.height >= len(m.lines) { return 1.0 } y := float64(m.offset) h := float64(m.height) t := float64(len(m.lines) - 1) v := y / (t - h) return math.Max(0.0, math.Min(1.0, v)) } func (m *model) maxYOffset() int { return max(0, len(m.lines)-m.height) } func (m *model) visibleLines() (lines []string) { if len(m.lines) > 0 { top := max(0, m.offset) bottom := clamp(m.offset+m.height, top, len(m.lines)) lines = m.lines[top:bottom] } return lines } func (m *model) SetOffset(n int) { m.offset = clamp(n, 0, m.maxYOffset()) } func (m *model) ViewDown() { if m.AtBottom() { return } m.SetOffset(m.offset + m.height) } func (m *model) ViewUp() { if m.AtTop() { return } m.SetOffset(m.offset - m.height) } func (m *model) HalfViewDown() { if m.AtBottom() { return } m.SetOffset(m.offset + m.height/2) } func (m *model) HalfViewUp() { if m.AtTop() { return } m.SetOffset(m.offset - m.height/2) } func (m *model) LineDown(n int) { if m.AtBottom() || n == 0 { return } // Make sure the Number of lines by which we're going to scroll isn't // greater than the Number of lines we actually have left before we reach // the bottom. m.SetOffset(m.offset + n) } func (m *model) LineUp(n int) { if m.AtTop() || n == 0 { return } // Make sure the Number of lines by which we're going to scroll isn't // greater than the Number of lines we are from the top. m.SetOffset(m.offset - n) } func (m *model) GotoTop() { if m.AtTop() { return } m.SetOffset(0) } func (m *model) GotoBottom() { m.SetOffset(m.maxYOffset()) } func (m *model) scrollDownToCursor() { at := m.cursorLineNumber() if m.offset <= at { // cursor is lower m.LineDown(max(0, at-(m.offset+m.height-1))) // minus one is due to cursorLineNumber() starts from 0 } else { m.SetOffset(at) } } func (m *model) scrollUpToCursor() { at := m.cursorLineNumber() if at < m.offset+m.height { // cursor is above m.LineUp(max(0, m.offset-at)) } else { m.SetOffset(at) } }