diff --git a/glyphs/glyphs.go b/glyphs/glyphs.go index 45421ab..4430169 100755 --- a/glyphs/glyphs.go +++ b/glyphs/glyphs.go @@ -17,7 +17,7 @@ import ( ) const ( - MaxGlyphsPerBatch = 16384 + DefaultGlyphsPerBatch = 16384 floatsPerGlyph = 13 invalidRune = unicode.ReplacementChar @@ -210,7 +210,7 @@ func (gr *GlyphRend) drawRune(run *TextRun, i int, prevRune rune, screenPos, pos //If we fill the buffer we issue a draw call gr.GlyphCount++ - if gr.GlyphCount == MaxGlyphsPerBatch { + if gr.GlyphCount == DefaultGlyphsPerBatch { gr.Draw() *bufIndex = 0 } @@ -487,7 +487,7 @@ func NewGlyphRend(fontFile string, fontOptions *truetype.Options, screenWidth, s gr := &GlyphRend{ GlyphCount: 0, - GlyphVBO: make([]float32, floatsPerGlyph*MaxGlyphsPerBatch), + GlyphVBO: make([]float32, floatsPerGlyph*DefaultGlyphsPerBatch), TextRunsBuf: make([]TextRun, 0, 20), SpacesPerTab: 4, } diff --git a/main.go b/main.go index 0a6c8d2..81c7822 100755 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( "os/exec" "runtime/pprof" "strings" + "sync" "time" "unicode/utf8" @@ -55,7 +56,8 @@ type program struct { gridMesh *meshes.Mesh gridMat *materials.Material - textBuf *ring.Buffer[byte] + textBuf *ring.Buffer[byte] + textBufMutex sync.Mutex cmdBuf []rune cmdBufLen int64 @@ -69,8 +71,7 @@ type program struct { maxLinesToShow int64 activeCmd *Cmd - - Settings *Settings + Settings *Settings } const ( @@ -230,10 +231,12 @@ func (p *program) Update() { p.MainUpdate() } -// @TODO: These probably need a mutex func (p *program) WriteToTextBuf(text []byte) { + // This is locked because running cmds are potentially writing to it same time we are + p.textBufMutex.Lock() p.textBuf.Write(text...) p.scrollPos = clamp(p.textBuf.Len-p.maxCharsToShow, 0, p.textBuf.Len-1) + p.textBufMutex.Unlock() } func (p *program) WriteToCmdBuf(text []rune) { @@ -277,7 +280,16 @@ func (p *program) MainUpdate() { } if mouseWheelYNorm := -int64(input.GetMouseWheelYNorm()); mouseWheelYNorm != 0 { - p.scrollPos = clamp(p.scrollPos+p.scrollSpd*mouseWheelYNorm, 0, p.textBuf.Len) + + var newPosNewLines int64 + w, _ := p.GridSize() + if mouseWheelYNorm < 0 { + newPosNewLines, _ = find_n_lines_index(p.textBuf.Data, p.scrollPos, p.scrollSpd*mouseWheelYNorm-1, int64(w)) + } else { + newPosNewLines, _ = find_n_lines_index(p.textBuf.Data, p.scrollPos, p.scrollSpd*mouseWheelYNorm, int64(w)) + } + + p.scrollPos = clamp(newPosNewLines, 0, p.textBuf.Len) } // Delete inputs @@ -290,7 +302,6 @@ func (p *program) MainUpdate() { p.DeleteNextChar() } - // @TODO cmds should be printed with only syntax highlighting // Draw textBuf v1, v2 := p.textBuf.Views() @@ -643,6 +654,10 @@ func (p *program) DrawCursor() { p.rend.Draw(p.gridMesh, gglm.NewTrMatId().Translate(pos).Scale(gglm.NewVec3(0.1*p.GlyphRend.Atlas.SpaceAdvance, p.GlyphRend.Atlas.LineHeight, 1)), p.gridMat) } +func (p *program) GridSize() (w, h int32) { + return p.GlyphRend.ScreenWidth / int32(p.GlyphRend.Atlas.SpaceAdvance), p.GlyphRend.ScreenHeight / int32(p.GlyphRend.Atlas.LineHeight) +} + func (p *program) ScreenPosToGridPos(screenPos *gglm.Vec3) { screenPos.SetX(screenPos.X() / p.GlyphRend.Atlas.SpaceAdvance * p.GlyphRend.Atlas.SpaceAdvance) screenPos.SetY(screenPos.Y() / p.GlyphRend.Atlas.LineHeight * p.GlyphRend.Atlas.LineHeight) @@ -699,11 +714,11 @@ func (p *program) DebugRender() { for i := 0; i < charsPerFrame/charCount; i++ { p.GlyphRend.DrawTextOpenGLAbsString(str, gglm.NewVec3(xOff, float32(p.GlyphRend.Atlas.LineHeight)*5+yOff, 0), &p.Settings.DefaultColor) } - p.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps, " Draws/f: ", math.Ceil(charsPerFrame/glyphs.MaxGlyphsPerBatch), " chars/f: ", charsPerFrame, " chars/s: ", fps*charsPerFrame)) + p.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps, " Draws/f: ", math.Ceil(charsPerFrame/glyphs.DefaultGlyphsPerBatch), " chars/f: ", charsPerFrame, " chars/s: ", fps*charsPerFrame)) } else { charsPerFrame := float64(charCount) p.GlyphRend.DrawTextOpenGLAbsString(str, gglm.NewVec3(xOff, float32(p.GlyphRend.Atlas.LineHeight)*5+yOff, 0), &p.Settings.DefaultColor) - p.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps, " Draws/f: ", math.Ceil(charsPerFrame/glyphs.MaxGlyphsPerBatch), " chars/f: ", int(charsPerFrame), " chars/s: ", fps*int(charsPerFrame))) + p.win.SDLWin.SetTitle(fmt.Sprint("FPS: ", fps, " Draws/f: ", math.Ceil(charsPerFrame/glyphs.DefaultGlyphsPerBatch), " chars/f: ", int(charsPerFrame), " chars/s: ", fps*int(charsPerFrame))) } } } @@ -769,3 +784,111 @@ func clamp[T constraints.Ordered](x, min, max T) T { return x } + +func FindNthOrLastIndex[T comparable](arr []T, x T, startIndex, n int64) (lastIndex int64) { + + lastIndex = -1 + if n >= 0 { + + for i := startIndex; i < int64(len(arr)); i++ { + + if arr[i] != x { + continue + } + lastIndex = i + + n-- + if n <= 0 { + return i + } + } + + } else { + + for i := startIndex; i >= 0; i-- { + + if arr[i] != x { + continue + } + lastIndex = i + + n++ + if n >= 0 { + return i + } + } + } + + return lastIndex +} + +func find_n_lines_index(arr []byte, startIndex, n, charsPerLine int64) (lastIndex, lastSize int64) { + + lastIndex = -1 + lastSize = 0 + if n >= 0 { + // @Note we should ignore zero width glyphs + // @Note is this better in glyphs package? + bytesSeen := int64(0) + arrSize := int64(len(arr)) + charsSeenThisLine := int64(0) + for startIndex+bytesSeen < arrSize { + + r, size := utf8.DecodeRune(arr[startIndex+bytesSeen:]) + if r == utf8.RuneError { + break + } + + charsSeenThisLine++ + bytesSeen += int64(size) + + // If this is true we covered one line + if charsSeenThisLine == charsPerLine || r == '\n' { + + charsSeenThisLine = 0 + lastSize = int64(size) + lastIndex = startIndex + bytesSeen + + n-- + if n <= 0 { + break + } + } + } + + } else { + + bytesSeen := int64(0) + charsSeenThisLine := int64(0) + for startIndex-bytesSeen > 0 { + + r, size := utf8.DecodeLastRune(arr[:startIndex-bytesSeen+1]) + if r == utf8.RuneError { + break + } + + charsSeenThisLine++ + bytesSeen += int64(size) + + // If this is true we covered one line + if charsSeenThisLine == charsPerLine || r == '\n' { + + charsSeenThisLine = 0 + lastSize = int64(size) + lastIndex = startIndex - bytesSeen + 1 + lastSize + + n++ + if n >= 0 { + break + } + } + } + + // Handle reaching beginning before finding nth line + if startIndex-bytesSeen == 0 { + return 0, 0 + } + } + + return lastIndex, lastSize +}