package document import ( "strings" "timshome.page/gilo/editor/highlight" "timshome.page/gilo/internal/gilo" "timshome.page/gilo/key" "unicode" ) type Row struct { chars []rune render []rune Hl []int } func newRow(s string) *Row { var chars []rune var render []rune for _, ch := range s { chars = append(chars, ch) render = append(render, ch) } return &Row{chars, render, []int{}} } func (r *Row) Size() int { return len(r.chars) } func (r *Row) RenderSize() int { return len(r.render) } func (r *Row) Render(at *gilo.Point) string { return string(r.render[at.X:]) } func (r *Row) Search(query string) int { return strings.Index(string(r.render), query) } func (r *Row) insertRune(ch rune, at int) { // If insertion index is invalid, just // append the rune to the end of the array if at < 0 || at >= r.Size() { r.chars = append(r.chars, ch) r.update() return } var newSlice []rune // Split the character array at the insertion point start := r.chars[0:at] end := r.chars[at:r.Size()] // Splice it back together newSlice = append(newSlice, start...) newSlice = append(newSlice, ch) newSlice = append(newSlice, end...) r.chars = newSlice r.update() } func (r *Row) appendString(str string) { for _, ch := range str { r.chars = append(r.chars, ch) } r.update() } func (r *Row) deleteRune(at int) { if at < 0 || at >= r.Size() { return } var newSlice []rune // Split the character array at the insertion point start := r.chars[0:at] end := r.chars[at+1 : r.Size()] // Skip the index in question // Splice it back together newSlice = append(newSlice, start...) newSlice = append(newSlice, end...) r.chars = newSlice r.update() } func (r *Row) update() { r.render = r.render[:0] replacement := strings.Repeat(" ", gilo.TabSize) str := strings.ReplaceAll(string(r.chars), "\t", replacement) for _, ch := range str { r.render = append(r.render, ch) } r.updateSyntax() } func (r *Row) updateSyntax() { i := 0 prevSep := true r.Hl = make([]int, r.RenderSize()) for i < r.RenderSize() { ch := r.render[i] prevHl := highlight.Normal if i > 0 { prevHl = r.Hl[i-1] } if (unicode.IsDigit(ch) && (prevSep || prevHl == highlight.Number)) || (ch == '.' && prevHl == highlight.Number) { r.Hl[i] = highlight.Number i += 1 prevSep = false continue } else { r.Hl[i] = highlight.Normal } prevSep = key.IsSeparator(ch) i++ } } func (r *Row) toString() string { return string(r.chars) } func (r *Row) CursorXToRenderX(cursorX int) (renderX int) { renderX = 0 for i := 0; i < cursorX; i++ { if r.chars[i] == '\t' { renderX += (gilo.TabSize - 1) - (renderX % gilo.TabSize) } renderX += 1 } return renderX } func (r *Row) RenderXtoCursorX(renderX int) (cursorX int) { currentRenderX := 0 cursorX = 0 for cursorX = 0; cursorX < r.Size(); cursorX++ { if r.chars[cursorX] == '\t' { currentRenderX += (gilo.TabSize - 1) - (currentRenderX % gilo.TabSize) } else { currentRenderX += 1 } if currentRenderX > renderX { return cursorX } } return cursorX }