diff --git a/main.go b/main.go index 78cde9f..f3e593c 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,33 @@ import ( "github.com/veandco/go-sdl2/ttf" ) +const ( + PURPLE = 0xDC42DDFF + GOLD = 0xD4AF37FF + RED = 0x51D718FF + ORANGE = 0xFF8300FF + DARKPURPLE = 0x8A6CD5FF + BEIGE = 0xF5A58DFF + BLUE = 0x0893FCFF + GREY = 0xC7D7CFFF + GREEN = 0x51D733FF + WHITE = 0xFFFFFFFF + LNNMBC = 0xbd93f9FF +) + +var Colors = map[textmanager.Term]uint32{ + textmanager.Normal: WHITE, + textmanager.Loop: PURPLE, + textmanager.Condition: BLUE, + textmanager.Type: DARKPURPLE, + textmanager.Bool: BEIGE, + textmanager.Digit: GREY, + textmanager.Return: ORANGE, + textmanager.String: GREEN, + textmanager.Comment: GOLD, + textmanager.Special: GOLD, +} + func hexToSdlColor(color uint32) sdl.Color { r, g, b, a := hexToRBGA(color) return sdl.Color{R: r, G: g, B: b, A: a} @@ -49,7 +76,9 @@ func GUIStop() { } type Cache struct { - PreRenderredCharTextures map[rune]CharTexture + PreRenderredCharTextures map[rune]map[uint32]CharTexture + + // TODO replace that PreRenderredNumsTextures map[rune]CharTexture RectangleMatrix *RectangleMatrix } @@ -60,7 +89,7 @@ type RectangleMatrix struct { Columns int32 } -func NewRectangleMatrix(rows, columns, fontSize, SpaceBetween int32) *RectangleMatrix { +func NewRectangleMatrix(rows, columns, fontSize, XBegin int32) *RectangleMatrix { rectangleMatrix := &RectangleMatrix{ Rows: rows, Columns: columns, @@ -68,7 +97,7 @@ func NewRectangleMatrix(rows, columns, fontSize, SpaceBetween int32) *RectangleM rectangleMatrix.RectangleMatrix = make([][]*sdl.Rect, rows) for i := int32(0); i < rows; i++ { for j := int32(0); j < columns; j++ { - rectangleMatrix.RectangleMatrix[i] = append(rectangleMatrix.RectangleMatrix[i], &sdl.Rect{X: SpaceBetween, Y: i * fontSize}) + rectangleMatrix.RectangleMatrix[i] = append(rectangleMatrix.RectangleMatrix[i], &sdl.Rect{X: XBegin, Y: i * fontSize}) } } return rectangleMatrix @@ -115,6 +144,9 @@ type Engine struct { LineNumbersColor uint32 TextBackgroundColor uint32 CursorColor uint32 + + // отступ дл нумирации строк на экране + CharCountInLineNumber int32 } func (e *Engine) RenderCursor(cursorId int64) { @@ -130,7 +162,7 @@ func (e *Engine) RenderCursor(cursorId int64) { cur := e.text.Cursors[i] //println("Cur head: ", cur.ScreenHead.RowNumber, "Cur tail: ", cur.ScreenTail.RowNumber) e.renderer.SetDrawColor(hexToRBGA(cur.Color)) - col := cur.Col + col := cur.Col - leftBorder // TODO отображать относительно Одного курсора, поэтому cur.ScreenHead.RowNumber не годится (относительно самого себя) row := cur.Row - cur.ScreenHead.RowNumber var padding int32 @@ -141,21 +173,19 @@ func (e *Engine) RenderCursor(cursorId int64) { } if col == -1 { - col = 4 - leftBorder + col = e.CharCountInLineNumber padding = 0 } else { - if col < leftBorder-1 { + if col < -1 { continue } - if col == leftBorder-1 && leftBorder != 0 { + if col == -1 && leftBorder != 0 { //println("\t\t\tLEFTBORDER") col++ padding = 0 - col -= leftBorder - col += 4 + col += e.CharCountInLineNumber } else { - col -= leftBorder - col += 4 + col += e.CharCountInLineNumber padding = e.GetRectFromMatrix(row, col).W } } @@ -224,6 +254,7 @@ func NewEngine(windowWidth, windowHeight int32, LineNumbersColor: lineNumbersColor, TextBackgroundColor: textBackgroundColor, CursorColor: cursorColor, + CharCountInLineNumber: 4, // значение по умолчанию, я так захотел!! } err = engine.SetFont(fontFilename, fontSize, fontSpaceBetween, fontColor, lineNumbersColor) @@ -239,7 +270,12 @@ func NewEngine(windowWidth, windowHeight int32, } // TODO server.createCursor(id) ?? - cur := textmanager.NewCursor(0, engine.cache.RectangleMatrix.Rows, engine.cache.RectangleMatrix.Columns) + cur := textmanager.NewCursor( + 0, + engine.cache.RectangleMatrix.Rows, + engine.cache.RectangleMatrix.Columns-engine.CharCountInLineNumber-2, // ещё -2 чтобы курсор влез на экран + ) + cur.LineIter = engine.text.GetHead() cur.CharIter = nil cur.Color = cursorColor @@ -285,11 +321,17 @@ func (e *Engine) Stop() { } if e.cache != nil { - for _, texture := range e.cache.PreRenderredCharTextures { - if texture.Texture == nil { + for _, mapColor := range e.cache.PreRenderredCharTextures { + if mapColor == nil { continue } - texture.Texture.Destroy() + for _, texture := range mapColor { + if texture.Texture == nil { + continue + } + + texture.Texture.Destroy() + } } } // TODO think about dele all text ??? @@ -304,24 +346,23 @@ func (e *Engine) SetFont(filename string, sizePx int32, spaceBetween int32, colo sizePt := int(float32(sizePx) * scaleFactor) //println(scaleFactor, sizePt) - var ttfFont *ttf.Font + var ttfFont *ttf.Font ttfFont, err = ttf.OpenFont(filename, sizePt) - if err != nil { - // open default font from memory - RWops, err := sdl.RWFromMem(FontBytes) - - if err != nil { - return err - } + if err != nil { + // open default font from memory + RWops, err := sdl.RWFromMem(FontBytes) - ttfFont, err = ttf.OpenFontRW(RWops, 1, sizePt) + if err != nil { + return err + } - if err != nil { - return err - } - } + ttfFont, err = ttf.OpenFontRW(RWops, 1, sizePt) + if err != nil { + return err + } + } if e.font.ttfFont != nil { e.font.ttfFont.Close() @@ -340,34 +381,34 @@ func (e *Engine) SetFont(filename string, sizePx int32, spaceBetween int32, colo } func getScaleFactor(fontFilename string) (float32, error) { - var ( - ttfFont *ttf.Font - RWops *sdl.RWops - err error - tmpSize = 100 - ) + var ( + ttfFont *ttf.Font + RWops *sdl.RWops + err error + tmpSize = 100 + ) ttfFont, err = ttf.OpenFont(fontFilename, tmpSize) - if err != nil { - // open default font from memory - RWops, err = sdl.RWFromMem(FontBytes) + if err != nil { + // open default font from memory + RWops, err = sdl.RWFromMem(FontBytes) - if err != nil { - return 1, err - } + if err != nil { + return 1, err + } - ttfFont, err = ttf.OpenFontRW(RWops, 1, tmpSize) + ttfFont, err = ttf.OpenFontRW(RWops, 1, tmpSize) - if err != nil { - return 1, err - } - } + if err != nil { + return 1, err + } + } defer ttfFont.Close() var maxH int32 = 0 for _, c := range AllSupportedChars { - surface, err := ttfFont.RenderGlyphBlended(c, sdl.Color{R: 0, G: 0, B: 0, A: 0}) + surface, err := ttfFont.RenderGlyphBlended(c, sdl.Color{R: 0, G: 0, B: 0, A: 0}) if err != nil { return 1, err } @@ -385,7 +426,10 @@ func (e *Engine) SetCache(supportedChars string) error { } cache := &Cache{} - cache.PreRenderredCharTextures = make(map[rune]CharTexture) + cache.PreRenderredCharTextures = make(map[rune]map[uint32]CharTexture) + for _, char := range supportedChars { + cache.PreRenderredCharTextures[char] = make(map[uint32]CharTexture) + } cache.PreRenderredNumsTextures = make(map[rune]CharTexture) var ( @@ -395,22 +439,26 @@ func (e *Engine) SetCache(supportedChars string) error { ) for _, char := range supportedChars { - fontSurface, _ := e.font.ttfFont.RenderGlyphBlended(char, e.font.GetColor()) - texture, _ := e.renderer.CreateTextureFromSurface(fontSurface) - cache.PreRenderredCharTextures[char] = CharTexture{texture, fontSurface.W} - if fontSurface.W < width { - width = fontSurface.W - } - if fontSurface.H > mx { - mx = fontSurface.H + for _, color := range Colors { + fontSurface, _ := e.font.ttfFont.RenderGlyphBlended(char, hexToSdlColor(color)) + texture, _ := e.renderer.CreateTextureFromSurface(fontSurface) + cache.PreRenderredCharTextures[char][color] = CharTexture{texture, fontSurface.W} + if fontSurface.W < width { + width = fontSurface.W + } + if fontSurface.H > mx { + mx = fontSurface.H + } } } + var digitWidth int32 // cache all digits for rendering Numbers of Rows for _, char := range "0123456789" { numsSurface, _ := e.font.ttfFont.RenderGlyphBlended(char, e.font.GetNumColor()) texture, _ := e.renderer.CreateTextureFromSurface(numsSurface) cache.PreRenderredNumsTextures[char] = CharTexture{texture, numsSurface.W} + digitWidth = numsSurface.W } var ( @@ -418,8 +466,8 @@ func (e *Engine) SetCache(supportedChars string) error { rows = h / height columns = w / (width + e.font.GetSpaceBetween()) ) - //println("h:", h, "mx:", mx, "height:", height) - cache.RectangleMatrix = NewRectangleMatrix(rows, columns, e.font.GetSize(), e.font.GetSpaceBetween()) + + cache.RectangleMatrix = NewRectangleMatrix(rows, columns, e.font.GetSize(), e.CharCountInLineNumber*digitWidth+e.font.GetSpaceBetween()+7) // +7 MUST BE HERE. Как в renderText padding = ... + 7 e.cache = cache return nil @@ -452,7 +500,7 @@ func (e *Engine) Loop() { if err != nil { //println(err) } - e.renderText(DEBUG_CUR_ID) + e.RenderText(DEBUG_CUR_ID) for running { event := sdl.WaitEvent() if event == nil { @@ -467,7 +515,7 @@ func (e *Engine) Loop() { case sdl.TextInputEvent: pressedKey := t.GetText() e.InsertChar(rune(pressedKey[0]), DEBUG_CUR_ID) - e.renderText(DEBUG_CUR_ID) + e.RenderText(DEBUG_CUR_ID) break case sdl.KeyboardEvent: // this branch active too when sdl.TextInputEvent @@ -494,7 +542,7 @@ func (e *Engine) Loop() { // TODO 4 -> SPACE_IN_ONE_TAB e.InsertChar('\t', DEBUG_CUR_ID) } - e.renderText(DEBUG_CUR_ID) + e.RenderText(DEBUG_CUR_ID) break } } @@ -532,6 +580,7 @@ func (e *Engine) EraseChar(key sdl.Scancode, cursorId int64) { switch key { case sdl.SCANCODE_BACKSPACE: err = e.text.RemoveCharBefore(cursorId) + //print("res: ", e.text.GetScreenString(0)) case sdl.SCANCODE_DELETE: err = e.text.RemoveCharAfter(cursorId) } @@ -563,78 +612,180 @@ func (e *Engine) InsertChar(value rune, cursorId int64) { } } -func (e *Engine) renderText(cursorId int64) { +func Contains(s []rune, e rune) bool { + for _, a := range s { + if a == e { + return true + } + } + return false +} + +// возвращает массив длины текста +// на i-ой позиции цвет i-го символа строки +// func HighlightTokensInString(s string) []uint32 { +// result := make([]uint32, len(s)) +// separators := []rune{' ', '\n', '(', '{', '}', ')', ';', '/', '"'} +// curStr := "" +// lineCommentFlag := 0 +// stringStartFlag := 0 +// for ind, char := range s { +// if stringStartFlag == 1 { +// if char == '"' { +// stringStartFlag = 0 +// } +// // гарантируется что существует предыдущий символ, т.к. в данной +// // ветке рассматривается случай, когда до текущего символа встретился '"' +// result[ind-1] = textmanager.GREEN +// result[ind] = textmanager.GREEN +// continue +// } + +// if char == '/' { +// lineCommentFlag++ +// } else if lineCommentFlag < 2 { +// lineCommentFlag = 0 +// } + +// if lineCommentFlag >= 2 { +// // гарантируется что существует предыдущий символ, т.к. в данной +// // ветке рассматривается случай, когда идёт два символа '\' подряд +// result[ind-1] = textmanager.GREY +// result[ind] = textmanager.GREY +// if char == '\n' { +// lineCommentFlag = 0 +// curStr = "" +// } +// continue +// } + +// if char == '"' { +// stringStartFlag = 1 +// } + +// result[ind] = textmanager.FNTCLR +// if Contains(separators, char) { +// if color, ok := textmanager.TokensColor[curStr]; ok { +// // start coloring +// for i := ind - len(curStr); i < ind; i += 1 { +// result[i] = color +// } +// } +// curStr = "" +// } else { +// curStr += string(char) +// } +// } +// if color, ok := textmanager.TokensColor[curStr]; ok { +// for i := len(s) - len(curStr); i < len(s); i += 1 { +// result[i] = color +// } +// } +// // assert(len(s) == len(result)) +// return result +// } + +// TODO переписать))) +func (e *Engine) RenderText(cursorId int64) { e.renderer.Clear() var ( // e.font.GetSpaceBetween равен 0, поэтому он сейчас ни на что не влияет || тут +7 px это просто отступ от первой цифры - paddingLeft int32 = (e.font.GetSpaceBetween()+e.cache.PreRenderredCharTextures[rune('1')].Width)*4 + 7 + col int32 = e.CharCountInLineNumber + paddingLeft int32 = (e.font.GetSpaceBetween()+e.cache.PreRenderredCharTextures[rune('1')][Colors[textmanager.Normal]].Width)*col + 7 X int32 = paddingLeft Y int32 = 0 row int32 = 0 // тут col равен 4 потому что мы 4 колоноки оставляем под нумерацию - col int32 = 4 // TODO относительные координаты курсора от экрана - delta int32 = e.text.Cursors[cursorId].Col - e.cache.RectangleMatrix.Columns + 2 + 4 + //delta int32 = e.text.Cursors[cursorId].Col - e.cache.RectangleMatrix.Columns + 2 + 4 ) e.renderBackground(paddingLeft) - //println("delta:", delta) - - leftBorder := e.text.Cursors[cursorId].ScreenLeft - //println("LeftBorderText: ", leftBorder) - if e.text.Cursors[cursorId].Col < leftBorder { - e.text.Cursors[cursorId].ScreenLeft = e.text.Cursors[cursorId].Col + 1 - delta = e.text.Cursors[cursorId].ScreenLeft - } else if delta >= leftBorder { - e.text.Cursors[cursorId].ScreenLeft = delta - } else { - delta = leftBorder - } - - if delta < 0 { - delta = 0 - } - - for _, c := range e.text.GetScreenString(0) { - if col-4 < delta { - if c == '\n' { - Y += e.font.GetSize() - X = paddingLeft + e.font.GetSpaceBetween() - row++ - col = 4 - } else { - col++ - } - continue - } - if col-delta+1 >= e.cache.RectangleMatrix.Columns { - if c == '\n' { - Y += e.font.GetSize() - X = paddingLeft + e.font.GetSpaceBetween() - row++ - col = 4 - } else { - col++ - } - continue - } - //println("col - del: ", col - delta, "c:", rune(c)) - e.GetRectFromMatrix(row, col-delta).H = e.font.GetSize() - e.GetRectFromMatrix(row, col-delta).W = e.cache.PreRenderredCharTextures[rune(c)].Width - e.GetRectFromMatrix(row, col-delta).X = X - e.GetRectFromMatrix(row, col-delta).Y = Y - e.renderer.Copy(e.cache.PreRenderredCharTextures[rune(c)].Texture, nil, e.GetRectFromMatrix(row, col-delta)) - X += e.cache.PreRenderredCharTextures[rune(c)].Width + e.font.GetSpaceBetween() - col++ + screenString, screenTerms := e.text.GetScreenStringWithTerms(0) + for i, c := range screenString { + t := screenTerms[i] if c == '\n' { Y += e.font.GetSize() X = paddingLeft + e.font.GetSpaceBetween() row++ - col = 4 + col = e.CharCountInLineNumber + continue + } + if col >= e.cache.RectangleMatrix.Columns { + continue // это из-за лишних чимволов которые появляются из-за отсптупа для номеров строк } + // TODO write setter for this -> + e.GetRectFromMatrix(row, col).H = e.font.GetSize() + e.GetRectFromMatrix(row, col).W = e.cache.PreRenderredCharTextures[rune(c)][Colors[t]].Width + e.GetRectFromMatrix(row, col).X = X + e.GetRectFromMatrix(row, col).Y = Y + e.renderer.Copy(e.cache.PreRenderredCharTextures[rune(c)][Colors[t]].Texture, nil, e.GetRectFromMatrix(row, col)) + X += e.cache.PreRenderredCharTextures[rune(c)][Colors[t]].Width + e.font.GetSpaceBetween() + col++ } + //println("delta:", delta) + /* + //leftBorder := e.text.Cursors[cursorId].ScreenLeft + //println("LeftBorderText: ", leftBorder) + if e.text.Cursors[cursorId].Col < leftBorder { + e.text.Cursors[cursorId].ScreenLeft = e.text.Cursors[cursorId].Col + 1 + delta = e.text.Cursors[cursorId].ScreenLeft + } else if delta >= leftBorder { + e.text.Cursors[cursorId].ScreenLeft = delta + } else { + delta = leftBorder + } + + if delta < 0 { + delta = 0 + } + // for tokens + var currentColor uint32 = textmanager.FNTCLR + + tokens := HighlightTokensInString(e.text.GetScreenString(0)) + for ind, c := range e.text.GetScreenString(0) { + currentColor = tokens[ind] + if col-4 < delta { + if c == '\n' { + Y += e.font.GetSize() + X = paddingLeft + e.font.GetSpaceBetween() + row++ + col = 4 + } else { + col++ + } + continue + } + if col-delta+1 >= e.cache.RectangleMatrix.Columns { + if c == '\n' { + Y += e.font.GetSize() + X = paddingLeft + e.font.GetSpaceBetween() + row++ + col = 4 + } else { + col++ + } + continue + } + //println("col - del: ", col - delta, "c:", rune(c)) + e.GetRectFromMatrix(row, col-delta).H = e.font.GetSize() + e.GetRectFromMatrix(row, col-delta).W = e.cache.PreRenderredCharTextures[rune(c)][currentColor].Width + e.GetRectFromMatrix(row, col-delta).X = X + e.GetRectFromMatrix(row, col-delta).Y = Y + e.renderer.Copy(e.cache.PreRenderredCharTextures[rune(c)][currentColor].Texture, nil, e.GetRectFromMatrix(row, col-delta)) + X += e.cache.PreRenderredCharTextures[rune(c)][currentColor].Width + e.font.GetSpaceBetween() + col++ + if c == '\n' { + Y += e.font.GetSize() + X = paddingLeft + e.font.GetSpaceBetween() + row++ + col = 4 + } + } + + */ cur := e.text.Cursors[cursorId] row = 0 col = 3 @@ -685,10 +836,10 @@ func (e *Engine) renderBackground(paddingLeft int32) { it would be more efficient to do this directly using the function SDL_RenderFillRect." */ e.renderLineNumbersBackground(paddingLeft) - e.renderTextBackground(paddingLeft) + e.RenderTextBackground(paddingLeft) } -func (e *Engine) renderTextBackground(paddingLeft int32) { +func (e *Engine) RenderTextBackground(paddingLeft int32) { w, h := e.window.GetSize() e.renderer.SetDrawColor(hexToRBGA(e.TextBackgroundColor)) @@ -727,8 +878,8 @@ func main() { FontSize int32 = 30 // in px! SpaceBetween int32 = 0 FontFilename string = "MonoNL-Regular.ttf" - FontColor uint32 = 0xFFFFFFFF - LineNumbersColor uint32 = 0xbd93f9FF + FontColor uint32 = Colors[textmanager.Normal] + LineNumbersColor uint32 = LNNMBC LineNumbersBackgroundColor uint32 = 0x44475aFF TextBackgroundColor uint32 = 0x282a36FF CursorColor uint32 = 0xDAD2D8FF diff --git a/textmanager/textmanager.go b/textmanager/textmanager.go index b8fd227..34abcfd 100644 --- a/textmanager/textmanager.go +++ b/textmanager/textmanager.go @@ -1,83 +1,119 @@ package textmanager import ( - "Type2gether/list" - "errors" + "Type2gether/list" + "errors" ) +type Term = byte + const ( - endl = '\n' + Normal Term = iota + Loop + Condition + Type + Bool + Digit + Return + Comment + String + Special ) -// type Line struct { -// list list.List[rune] -// } -// Cursor указывает на текущий элемент, а не на злемент за ним -// Line теперь представляем не как наследуемую структуру от list.List[rune], а как list.List[rune] сам по себе. -// чтобы создать новую строку, надо использовать line := new(list.List[rune]) -// все вызовы остаются, как прежде - -type Cursor struct { - LineIter *list.Node[*list.List[rune]] - CharIter *list.Node[rune] - Id int64 - Row int32 - Col int32 - Color uint32 +var ( + TokensTerm = map[string]Term{ + "for": Loop, + "while": Loop, + "if": Condition, + "else": Condition, + "return": Return, + "int": Type, + "long": Type, + "bool": Type, + "true": Bool, + "false": Bool, + "aboba": Special, + } + // MaxTokenLength int TODO: подумать над использованием +) - ScreenLeft int32 +type Char struct { + Value rune + TermType Term +} - ScreenHead *Border - ScreenTail *Border - ScreenRow int32 - ScreenCol int32 +type Cursor struct { + LineIter *list.Node[*list.List[Char]] + CharIter *list.Node[Char] + Id int64 + Row int32 + Col int32 + Color uint32 + + ScreenLeft int32 + + ScreenHead *Border + ScreenTail *Border + ScreenRowsCount int32 + ScreenColsCount int32 } //Эта структура нужна для скрола //Эта структура нужна для отрисовки нумерации строк type Border struct { - LineIter *list.Node[*list.List[rune]] - RowNumber int32 + LineIter *list.Node[*list.List[Char]] + RowNumber int32 } func (border *Border) Up() error { - if border.LineIter == nil { - return errors.New("lineIter is nil") - } + if border.LineIter == nil { + return errors.New("lineIter is nil") + } - if border.LineIter.GetPrev() == nil { - return errors.New("the first line") - } + if border.LineIter.GetPrev() == nil { + return errors.New("the first line") + } - border.LineIter = border.LineIter.GetPrev() - border.RowNumber-- - return nil + border.LineIter = border.LineIter.GetPrev() + border.RowNumber-- + return nil } func (border *Border) Down() error { - if border.LineIter == nil { - return errors.New("lineIter is nil") - } + if border.LineIter == nil { + return errors.New("lineIter is nil") + } - if border.LineIter.GetNext() == nil { - return errors.New("last line") - } + if border.LineIter.GetNext() == nil { + return errors.New("last line") + } - border.LineIter = border.LineIter.GetNext() - border.RowNumber++ - return nil + border.LineIter = border.LineIter.GetNext() + border.RowNumber++ + return nil } func (cur *Cursor) ScrollUp() { - if err := cur.ScreenHead.Up(); err == nil { - cur.ScreenTail.Up() - } + if err := cur.ScreenHead.Up(); err == nil { + cur.ScreenTail.Up() + } } func (cur *Cursor) ScrollDown() { - if err := cur.ScreenTail.Down(); err == nil { - cur.ScreenHead.Down() - } + if err := cur.ScreenTail.Down(); err == nil { + cur.ScreenHead.Down() + } +} + +func (cur *Cursor) FixLeftScreen() { + if cur.ScreenLeft-1 > cur.Col { + cur.ScreenLeft = cur.Col + 1 + } else if cur.Col-cur.ScreenLeft > cur.ScreenColsCount { + cur.ScreenLeft = cur.Col - cur.ScreenColsCount + } + if cur.ScreenLeft < 0 { + cur.ScreenLeft = 0 + } } /* @@ -87,54 +123,51 @@ type Line struct { width int32 } */ + type Text struct { - list.List[*list.List[rune]] - // скорее всего не нужен тк в List есть аттрибут lenght / size int64 - Cursors []*Cursor + list.List[*list.List[Char]] + // скорее всего не нужен тк в List есть аттрибут lenght / size int64 + Cursors []*Cursor } func NewText() *Text { - t := new(Text) - t.PushBack(new(list.List[rune])) - // for _, cur := range t.Cursors { - // cur.ScreenHead.LineIter = t.GetHead() - // cur.ScreenTail.LineIter = t.GetTail() - // } - return t + t := new(Text) + t.PushBack(new(list.List[Char])) + return t } // TODO написать присвоение id для курсора // TODO на данном этапе при создании NewCursor просто всегда будем 0 в него передавать, когда будем писать серверную часть нужно будет определиться с созданием Id -func NewCursor(Id int64, ScreenRow, ScreenCol int32) *Cursor { - c := new(Cursor) - c.Row = 0 - c.Col = -1 - c.Id = Id - c.ScreenRow = ScreenRow - c.ScreenCol = ScreenCol - c.ScreenLeft = 0 - c.ScreenHead = &Border{RowNumber: 0} - c.ScreenTail = &Border{RowNumber: 0} - return c +func NewCursor(Id int64, ScreenRowsCount, ScreenColsCount int32) *Cursor { + c := new(Cursor) + c.Row = 0 + c.Col = -1 + c.Id = Id + c.ScreenRowsCount = ScreenRowsCount + c.ScreenColsCount = ScreenColsCount + c.ScreenLeft = 0 + c.ScreenHead = &Border{RowNumber: 0} + c.ScreenTail = &Border{RowNumber: 0} + return c } func (t *Text) SetCursorStartPosition(cursorId int64) { - cur := t.Cursors[cursorId] - cur.Row = 0 - cur.LineIter = t.GetHead() - - cur.Col = -1 - cur.CharIter = nil - - cur.ScreenLeft = 0 - cur.ScreenHead = &Border{LineIter: t.GetHead(), RowNumber: 0} // Егор сказал, что выстрелит, но куда... (создаем новый объект, а не меняем старый) - - cur.ScreenTail = &Border{LineIter: t.GetHead(), RowNumber: 0} - for i := 1; i < int(t.Cursors[cursorId].ScreenRow); i++ { - if err := cur.ScreenTail.Down(); err != nil { - return - } - } + cur := t.Cursors[cursorId] + cur.Row = 0 + cur.LineIter = t.GetHead() + + cur.Col = -1 + cur.CharIter = nil + + cur.ScreenLeft = 0 + cur.ScreenHead = &Border{LineIter: t.GetHead(), RowNumber: 0} // Егор сказал, что выстрелит, но куда... (создаем новый объект, а не меняем старый) + + cur.ScreenTail = &Border{LineIter: t.GetHead(), RowNumber: 0} + for i := 1; i < int(t.Cursors[cursorId].ScreenRowsCount); i++ { + if err := cur.ScreenTail.Down(); err != nil { + return + } + } } // TODO сделать красиво =) @@ -143,439 +176,648 @@ func (t *Text) SetCursorStartPosition(cursorId int64) { // иначе откатываем только ячейку func (t *Text) InsertCharBefore(cursorId int64, value rune) error { - err := t.InsertCharAfter(cursorId, value) - if err != nil { - return err - } - t.Cursors[cursorId].MoveLeft() - return nil + err := t.InsertCharAfter(cursorId, value) + if err != nil { + return err + } + t.Cursors[cursorId].MoveLeft() + return nil } func (t *Text) InsertCharAfter(cursorId int64, value rune) error { - cur := t.Cursors[cursorId] - - if cur.CharIter == nil { - err := cur.LineIter.GetValue().PushFront(value) - - if err != nil { - //printtln(err) - return err - } - - cur.CharIter = cur.LineIter.GetValue().GetHead() - cur.Col = 0 - return nil - } + cur := t.Cursors[cursorId] + + if cur.CharIter == nil { + err := cur.LineIter.GetValue().PushFront(Char{Value: value, TermType: Normal}) + + if err != nil { + //printtln(err) + return err + } + + cur.CharIter = cur.LineIter.GetValue().GetHead() + cur.Col = 0 + t.DetectTerms(cursorId) + // пересчитываем screen left + cur.FixLeftScreen() + return nil + } + + err := cur.LineIter.GetValue().InsertAfter(Char{Value: value, TermType: Normal}, cur.CharIter) + + if err != nil { + return err + } + + cur.CharIter = cur.CharIter.GetNext() + cur.Col++ + t.DetectTerms(cursorId) + // пересчитываем screen left + cur.FixLeftScreen() + return nil +} - err := cur.LineIter.GetValue().InsertAfter(value, cur.CharIter) +func Contains(s []rune, e rune) bool { + for _, a := range s { + if a == e { + return true + } + } + return false +} - if err != nil { - return err - } +func (t *Text) DetectTerms(cursorId int64) error { + if t.Cursors[cursorId].CharIter == nil { + return errors.New("CharIter is nil") + } + + var ( + ptr = t.Cursors[cursorId].LineIter.GetValue().GetHead() + prev *list.Node[Char] = nil + separators = []rune{' ', '\n', '(', '{', '}', ')', ';', '/', '"'} + curStr = "" + lineCommentFlag = 0 + stringStartFlag = 0 + ) + + for ptr != nil { + char := ptr.GetValue().Value + if stringStartFlag == 1 { + if char == '"' { + stringStartFlag = 0 + } + // гарантируется что существует предыдущий символ, т.к. в данной + // ветке рассматривается случай, когда до текущего символа встретился '"' + + SetTermType(ptr, String) + SetTermType(ptr.GetPrev(), String) + prev = ptr + ptr = ptr.GetNext() + continue + } + + if char == '/' { + lineCommentFlag++ + } else if lineCommentFlag < 2 { + lineCommentFlag = 0 + } + + if lineCommentFlag >= 2 { + // гарантируется что существует предыдущий символ, т.к. в данной + // ветке рассматривается случай, когда идёт два символа '\' подряд + SetTermType(ptr, Comment) + SetTermType(ptr.GetPrev(), Comment) + + if char == '\n' { + lineCommentFlag = 0 + curStr = "" + } + prev = ptr + ptr = ptr.GetNext() + continue + } + + if char == '"' { + stringStartFlag = 1 + } + + SetTermType(ptr, Normal) + + if Contains(separators, char) { + if term, ok := TokensTerm[curStr]; ok { + backPtr := ptr.GetPrev() + for i := 0; i < len(curStr); i++ { + SetTermType(backPtr, term) + backPtr = backPtr.GetPrev() + } + } + curStr = "" + } else { + curStr += string(char) + } + prev = ptr + ptr = ptr.GetNext() + } + + if prev == nil { + return nil + } + + if term, ok := TokensTerm[curStr]; ok { + backPtr := prev + for i := 0; i < len(curStr); i++ { + SetTermType(backPtr, term) + backPtr = backPtr.GetPrev() + } + } + return nil +} - cur.CharIter = cur.CharIter.GetNext() - cur.Col++ - return nil +func SetTermType(ptr *list.Node[Char], term Term) { + cur := ptr.GetValue() + cur.TermType = term + ptr.SetValue(cur) } // TODO validation func (t *Text) Paste(data string, cursorId int64) error { - //cur := t.Cursors[cursorId] - var err error - //println("data: ", data) - // TODO замена на before и перевернуть цикл - for _, e := range data { - if e == '\n' { - //println("\\n") - err = t.InsertLineAfter(cursorId) - } else { - err = t.InsertCharAfter(cursorId, e) - } - - if err != nil { - return err - } - } - - return nil + //cur := t.Cursors[cursorId] + var err error + //println("data: ", data) + // TODO замена на before и перевернуть цикл + for _, e := range data { + if e == '\n' { + //println("\\n") + err = t.InsertLineAfter(cursorId) + } else { + err = t.InsertCharAfter(cursorId, e) + } + + if err != nil { + return err + } + } + + return nil } func (t *Text) InsertLineAfter(cursorId int64) error { - cur := t.Cursors[cursorId] - - if cur.CharIter == nil { - line := new(list.List[rune]) - if cur.LineIter.GetValue().GetTail() != nil { - // line isn't empty - err := t.InsertAfter(line, cur.LineIter) - - if err != nil { - return err - } - - if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRow { - if cur.LineIter == cur.ScreenTail.LineIter { - cur.ScrollDown() - } else { - cur.ScreenTail.Up() - cur.ScreenTail.RowNumber++ - } - } else { - if cur.LineIter == cur.ScreenTail.LineIter { - cur.ScreenTail.Down() - } else { - cur.ScreenTail.RowNumber++ - } - } - - cur.LineIter.GetValue().CopyTo(cur.LineIter.GetNext().GetValue()) - cur.LineIter.GetValue().Clear() - - cur.LineIter = cur.LineIter.GetNext() - cur.CharIter = nil - - // cur.ScrollDown() - //printtln("head & tail", cur.ScreenHead.RowNumber, cur.ScreenTail.RowNumber) - - cur.Row++ - cur.Col = -1 - return nil - } - // line is empty - err := t.InsertAfter(line, cur.LineIter) - - if err != nil { - return err - } - - if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRow { - if cur.LineIter == cur.ScreenTail.LineIter { - cur.ScrollDown() - } else { - cur.ScreenTail.Up() - cur.ScreenTail.RowNumber++ - } - } else { - if cur.LineIter == cur.ScreenTail.LineIter { - cur.ScreenTail.Down() - } else { - cur.ScreenTail.RowNumber++ - } - } - - cur.LineIter = cur.LineIter.GetNext() - cur.CharIter = cur.LineIter.GetValue().GetHead() - - // cur.ScrollDown() - - cur.Row++ - cur.Col = -1 - return nil - } - - oldLineIter := cur.LineIter - lst, err := oldLineIter.GetValue().Split(cur.CharIter.GetNext()) - - if err != nil { - return err - } - - node := &list.Node[*list.List[rune]]{} - node.SetValue(lst) - - node.SetPrev(oldLineIter) - node.SetNext(oldLineIter.GetNext()) - if oldLineIter.GetNext() != nil { - oldLineIter.GetNext().SetPrev(node) - } else { - t.SetTail(node) - } - oldLineIter.SetNext(node) - - if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRow { - if cur.LineIter == cur.ScreenTail.LineIter { - cur.ScrollDown() - } else { - cur.ScreenTail.Up() - cur.ScreenTail.RowNumber++ - } - } else { - if cur.LineIter == cur.ScreenTail.LineIter { - cur.ScreenTail.Down() - } else { - cur.ScreenTail.RowNumber++ - } - } - - cur.LineIter = node - cur.CharIter = nil - - // cur.ScrollDown() - - cur.Row++ - cur.Col = -1 - return nil + cur := t.Cursors[cursorId] + + if cur.CharIter == nil { + line := new(list.List[Char]) + if cur.LineIter.GetValue().GetTail() != nil { + // line isn't empty + err := t.InsertAfter(line, cur.LineIter) + + if err != nil { + return err + } + + if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRowsCount { + if cur.LineIter == cur.ScreenTail.LineIter { + cur.ScrollDown() + } else { + cur.ScreenTail.Up() + cur.ScreenTail.RowNumber++ + } + } else { + if cur.LineIter == cur.ScreenTail.LineIter { + cur.ScreenTail.Down() + } else { + cur.ScreenTail.RowNumber++ + } + } + + cur.LineIter.GetValue().CopyTo(cur.LineIter.GetNext().GetValue()) + cur.LineIter.GetValue().Clear() + + cur.LineIter = cur.LineIter.GetNext() + cur.CharIter = nil + + // cur.ScrollDown() + //printtln("head & tail", cur.ScreenHead.RowNumber, cur.ScreenTail.RowNumber) + + cur.Row++ + cur.Col = -1 + // пересчитываем screen left + cur.FixLeftScreen() + return nil + } + // line is empty + err := t.InsertAfter(line, cur.LineIter) + + if err != nil { + return err + } + + if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRowsCount { + if cur.LineIter == cur.ScreenTail.LineIter { + cur.ScrollDown() + } else { + cur.ScreenTail.Up() + cur.ScreenTail.RowNumber++ + } + } else { + if cur.LineIter == cur.ScreenTail.LineIter { + cur.ScreenTail.Down() + } else { + cur.ScreenTail.RowNumber++ + } + } + + cur.LineIter = cur.LineIter.GetNext() + cur.CharIter = cur.LineIter.GetValue().GetHead() + + // cur.ScrollDown() + + cur.Row++ + cur.Col = -1 + // пересчитываем screen left + cur.FixLeftScreen() + return nil + } + + oldLineIter := cur.LineIter + lst, err := oldLineIter.GetValue().Split(cur.CharIter.GetNext()) + + if err != nil { + return err + } + + node := &list.Node[*list.List[Char]]{} + node.SetValue(lst) + + node.SetPrev(oldLineIter) + node.SetNext(oldLineIter.GetNext()) + if oldLineIter.GetNext() != nil { + oldLineIter.GetNext().SetPrev(node) + } else { + t.SetTail(node) + } + oldLineIter.SetNext(node) + + if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRowsCount { + if cur.LineIter == cur.ScreenTail.LineIter { + cur.ScrollDown() + } else { + cur.ScreenTail.Up() + cur.ScreenTail.RowNumber++ + } + } else { + if cur.LineIter == cur.ScreenTail.LineIter { + cur.ScreenTail.Down() + } else { + cur.ScreenTail.RowNumber++ + } + } + + cur.LineIter = node + cur.CharIter = nil + + // cur.ScrollDown() + + cur.Row++ + cur.Col = -1 + // пересчитываем screen left + cur.FixLeftScreen() + return nil } // TODO cur.CharIter == nil не значит, что строка пустая || Нужно проверить везде в коде это func (t *Text) RemoveCharBefore(cursorId int64) error { - cur := t.Cursors[cursorId] - - if cur.LineIter == nil { - //printtln("Press 'F', пацан к успеху шёл, Какой успех, раздался смех") - return nil - } - - if cur.CharIter == nil { - if cur.LineIter.GetPrev() == nil { - return nil - } - - if cur.ScreenTail.LineIter == cur.ScreenHead.LineIter { - cur.ScrollUp() - } else if cur.ScreenTail.LineIter == cur.LineIter { - if cur.LineIter.GetNext() == nil { - cur.ScreenTail.Up() - } else { - if err := cur.ScreenTail.Down(); err == nil { - cur.ScreenTail.RowNumber-- - } - } - } else if cur.ScreenHead.LineIter == cur.LineIter { - if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRow { - if err := cur.ScreenHead.Up(); err == nil { - cur.ScreenTail.RowNumber-- - } - } else { - if err := cur.ScreenHead.Up(); err == nil { - cur.ScreenTail.RowNumber-- - } - } - } else { - if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRow { - err := cur.ScreenTail.Down() - if err != nil { - } - cur.ScreenTail.RowNumber-- - } else { - cur.ScreenTail.RowNumber-- - } - } - - prev := cur.LineIter.GetPrev() - oldTail := prev.GetValue().GetTail() - oldLen := prev.GetValue().Length() - err := t.MergeLines(cursorId) - if err != nil { - return err - } - cur.LineIter = prev - cur.CharIter = oldTail - - cur.Row-- - cur.Col = oldLen - 1 - - return nil - } - - newCharIter := cur.CharIter.GetPrev() - err := cur.LineIter.GetValue().Remove(cur.CharIter) - - if err != nil { - return err - } - - cur.CharIter = newCharIter - cur.Col-- - - return nil + cur := t.Cursors[cursorId] + + if cur.LineIter == nil { + //printtln("Press 'F', пацан к успеху шёл, Какой успех, раздался смех") + return nil + } + + if cur.CharIter == nil { + if cur.LineIter.GetPrev() == nil { + return nil + } + + if cur.ScreenTail.LineIter == cur.ScreenHead.LineIter { + cur.ScrollUp() + } else if cur.ScreenTail.LineIter == cur.LineIter { + if cur.LineIter.GetNext() == nil { + cur.ScreenTail.Up() + } else { + if err := cur.ScreenTail.Down(); err == nil { + cur.ScreenTail.RowNumber-- + } + } + } else if cur.ScreenHead.LineIter == cur.LineIter { + if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRowsCount { + if err := cur.ScreenHead.Up(); err == nil { + cur.ScreenTail.RowNumber-- + } + } else { + if err := cur.ScreenHead.Up(); err == nil { + cur.ScreenTail.RowNumber-- + } + } + } else { + if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRowsCount { + err := cur.ScreenTail.Down() + if err != nil { + } + cur.ScreenTail.RowNumber-- + } else { + cur.ScreenTail.RowNumber-- + } + } + + prev := cur.LineIter.GetPrev() + oldTail := prev.GetValue().GetTail() + oldLen := prev.GetValue().Length() + err := t.MergeLines(cursorId) + if err != nil { + return err + } + cur.LineIter = prev + cur.CharIter = oldTail + + cur.Row-- + cur.Col = oldLen - 1 + + t.DetectTerms(cursorId) + // пересчитываем screen left + cur.FixLeftScreen() + return nil + } + + newCharIter := cur.CharIter.GetPrev() + err := cur.LineIter.GetValue().Remove(cur.CharIter) + + if err != nil { + return err + } + + cur.CharIter = newCharIter + cur.Col-- + + t.DetectTerms(cursorId) + // пересчитываем screen left + cur.FixLeftScreen() + + return nil } func (t *Text) RemoveCharAfter(cursorId int64) error { - cur := t.Cursors[cursorId] - oldCharIter := cur.CharIter - oldLineIter := cur.LineIter - cur.MoveRight() - if oldCharIter != cur.CharIter || oldLineIter != cur.LineIter { - err := t.RemoveCharBefore(cursorId) - return err - } - return nil + cur := t.Cursors[cursorId] + oldCharIter := cur.CharIter + oldLineIter := cur.LineIter + cur.MoveRight() + if oldCharIter != cur.CharIter || oldLineIter != cur.LineIter { + err := t.RemoveCharBefore(cursorId) + return err + } + return nil } // курсор не двигается!! func (t *Text) MergeLines(cursorId int64) error { - cur := t.Cursors[cursorId] - if cur.LineIter.GetPrev() == nil { - return errors.New("there is no previous line") - } - err := cur.LineIter.GetPrev().GetValue().Merge(cur.LineIter.GetValue()) - if err != nil { - return err - } - return t.Remove(cur.LineIter) + cur := t.Cursors[cursorId] + if cur.LineIter.GetPrev() == nil { + return errors.New("there is no previous line") + } + err := cur.LineIter.GetPrev().GetValue().Merge(cur.LineIter.GetValue()) + if err != nil { + return err + } + return t.Remove(cur.LineIter) } func (t *Text) GetString() string { - str := "" - iter := t.GetHead() - for iter != nil { - charIter := iter.GetValue().GetHead() - for charIter != nil { - str += string(charIter.GetValue()) - charIter = charIter.GetNext() - } - iter = iter.GetNext() - if iter != nil { - str += "\n" - } - } - ////printtln("Original str: ", str) - return str + str := "" + iter := t.GetHead() + for iter != nil { + charIter := iter.GetValue().GetHead() + for charIter != nil { + str += string(charIter.GetValue().Value) + charIter = charIter.GetNext() + } + iter = iter.GetNext() + if iter != nil { + str += "\n" + } + } + ////printtln("Original str: ", str) + return str } -// TODO think about width func (t *Text) GetScreenString(cursorId int32) string { - cur := t.Cursors[cursorId] - //printtln(cur.ScreenHead.LineIter, cur.ScreenTail.LineIter) - if cur.ScreenHead.LineIter == nil || cur.ScreenTail.LineIter == nil { - //printtln("ScreenHead is nil") - return "" - } - ptr1 := cur.ScreenHead.LineIter - //printtln(cur.ScreenHead, cur.ScreenTail) - res := "" - for ptr1 != nil && ptr1 != cur.ScreenTail.LineIter.GetNext() { - ptr2 := ptr1.GetValue().GetHead() - for ptr2 != nil { - res += string(ptr2.GetValue()) - ptr2 = ptr2.GetNext() - } - res += "\n" - ptr1 = ptr1.GetNext() - } - //printtln("res: ", res) - return res + cur := t.Cursors[cursorId] + if cur.ScreenHead.LineIter == nil || cur.ScreenTail.LineIter == nil { + //printtln("ScreenHead is nil") + return "" + } + ptr1 := cur.ScreenHead.LineIter + //printtln(cur.ScreenHead, cur.ScreenTail) + res := "" + // итерируемся по каждой строчке + for ptr1 != nil && ptr1 != cur.ScreenTail.LineIter.GetNext() { + ptr2 := ptr1.GetValue().GetHead() + // смотрим, если строка не пустая и в ней достаточно символов + // чтобы их было видно на экране в данный момент + if ptr2 != nil && ptr1.GetValue().Length() >= cur.ScreenLeft { + // начинаем брать строчку с первого символа, который попадает на экран + ptr2 = ptr1.GetValue().GetNodeByIndex(cur.ScreenLeft) + currentLen := int32(0) + // выдаём либо все символы строки, которые помещаются на экран + for ptr2 != nil && currentLen <= cur.ScreenColsCount { + currentLen++ + res += string(ptr2.GetValue().Value) + ptr2 = ptr2.GetNext() + } + } + res += "\n" + ptr1 = ptr1.GetNext() + } + return res } -func (cur *Cursor) MoveLeft() { - if cur.CharIter == nil { - if cur.LineIter.GetPrev() == nil { - return - } - - if cur.ScreenHead.LineIter == cur.LineIter { - if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRow { - cur.ScrollUp() - } else { - cur.ScreenHead.Up() - } - } - - cur.LineIter = cur.LineIter.GetPrev() - cur.CharIter = cur.LineIter.GetValue().GetTail() - cur.Row-- - - cur.Col = cur.LineIter.GetValue().Length() - 1 - return - } +func (t *Text) GetScreenStringWithTerms(cursorId int32) (string, []Term) { + cur := t.Cursors[cursorId] + if cur.ScreenHead.LineIter == nil || cur.ScreenTail.LineIter == nil { + //printtln("ScreenHead is nil") + return "", []Term{} + } + ptr1 := cur.ScreenHead.LineIter + //printtln(cur.ScreenHead, cur.ScreenTail) + res := "" + terms := []Term{} + // итерируемся по каждой строчке + for ptr1 != nil && ptr1 != cur.ScreenTail.LineIter.GetNext() { + ptr2 := ptr1.GetValue().GetHead() + // смотрим, если строка не пустая и в ней достаточно символов + // чтобы их было видно на экране в данный момент + if ptr2 != nil && ptr1.GetValue().Length() >= cur.ScreenLeft { + // начинаем брать строчку с первого символа, который попадает на экран + ptr2 = ptr1.GetValue().GetNodeByIndex(cur.ScreenLeft) + currentLen := int32(0) + // выдаём либо все символы строки, которые помещаются на экран + for ptr2 != nil && currentLen <= cur.ScreenColsCount { + currentLen++ + res += string(ptr2.GetValue().Value) + terms = append(terms, ptr2.GetValue().TermType) + ptr2 = ptr2.GetNext() + } + } + res += "\n" + terms = append(terms, Normal) + ptr1 = ptr1.GetNext() + } + return res, terms +} - cur.CharIter = cur.CharIter.GetPrev() - cur.Col-- +/* +// TODO think about width +func (t *Text) GetScreenString(cursorId int32) string { + cur := t.Cursors[cursorId] + //printtln(cur.ScreenHead.LineIter, cur.ScreenTail.LineIter) + if cur.ScreenHead.LineIter == nil || cur.ScreenTail.LineIter == nil { + //printtln("ScreenHead is nil") + return "" + } + ptr1 := cur.ScreenHead.LineIter + //printtln(cur.ScreenHead, cur.ScreenTail) + res := "" + for ptr1 != nil && ptr1 != cur.ScreenTail.LineIter.GetNext() { + ptr2 := ptr1.GetValue().GetHead() + for ptr2 != nil { + res += string(ptr2.GetValue().Value) + ptr2 = ptr2.GetNext() + } + res += "\n" + ptr1 = ptr1.GetNext() + } + //printtln("res: ", res) + return res +} +*/ +func (cur *Cursor) MoveLeft() { + if cur.CharIter == nil { // значит мы стоим в начале строчки + if cur.LineIter.GetPrev() == nil { // значит мы стоим вверху текста + return + } + + if cur.ScreenHead.LineIter == cur.LineIter { + if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRowsCount { + cur.ScrollUp() + } else { + cur.ScreenHead.Up() + } + } + + cur.LineIter = cur.LineIter.GetPrev() + cur.CharIter = cur.LineIter.GetValue().GetTail() + cur.Row-- + + cur.Col = cur.LineIter.GetValue().Length() - 1 + + // пересчитываем screen left + cur.FixLeftScreen() + + return + } + + cur.CharIter = cur.CharIter.GetPrev() + cur.Col-- + // пересчитываем screen left + cur.FixLeftScreen() } func (cur *Cursor) MoveRight() { - if cur.CharIter == nil { - if cur.LineIter.GetValue().GetHead() == nil { - if cur.LineIter.GetNext() == nil { - return - } - - if cur.ScreenTail.LineIter == cur.LineIter { - if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRow { - cur.ScrollDown() - } - } - - cur.LineIter = cur.LineIter.GetNext() - cur.CharIter = nil - cur.Row++ - cur.Col = -1 - return - } - - cur.CharIter = cur.LineIter.GetValue().GetHead() - cur.Col = 0 - return - } - - if cur.CharIter == cur.LineIter.GetValue().GetTail() { - if cur.LineIter.GetNext() == nil { - return - } - - if cur.ScreenTail.LineIter == cur.LineIter { - if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRow { - cur.ScrollDown() - } - } - - cur.LineIter = cur.LineIter.GetNext() - cur.CharIter = nil - cur.Row++ - cur.Col = -1 - return - } - - cur.CharIter = cur.CharIter.GetNext() - cur.Col++ + if cur.CharIter == nil { + if cur.LineIter.GetValue().GetHead() == nil { + if cur.LineIter.GetNext() == nil { + return + } + + if cur.ScreenTail.LineIter == cur.LineIter { + if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRowsCount { + cur.ScrollDown() + } + } + + cur.LineIter = cur.LineIter.GetNext() + cur.CharIter = nil + cur.Row++ + cur.Col = -1 + + // пересчитываем screen left + cur.FixLeftScreen() + return + } + + cur.CharIter = cur.LineIter.GetValue().GetHead() + cur.Col = 0 + // пересчитываем screen left + cur.FixLeftScreen() + return + } + + if cur.CharIter == cur.LineIter.GetValue().GetTail() { + if cur.LineIter.GetNext() == nil { + return + } + + if cur.ScreenTail.LineIter == cur.LineIter { + if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRowsCount { + cur.ScrollDown() + } + } + + cur.LineIter = cur.LineIter.GetNext() + cur.CharIter = nil + cur.Row++ + cur.Col = -1 + // пересчитываем screen left + cur.FixLeftScreen() + return + } + + cur.CharIter = cur.CharIter.GetNext() + cur.Col++ + // пересчитываем screen left + cur.FixLeftScreen() } func (cur *Cursor) MoveUp() { - if cur.LineIter.GetPrev() == nil { - return - } - - if cur.ScreenHead.LineIter == cur.LineIter { - if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRow { - cur.ScrollUp() - } else { - cur.ScreenHead.Up() - } - } - - index := cur.LineIter.GetValue().Index(cur.CharIter) - cur.CharIter = cur.LineIter.GetPrev().GetValue().GetNodeByIndex(index) - cur.LineIter = cur.LineIter.GetPrev() - cur.Row-- - cur.Col = cur.LineIter.GetValue().Index(cur.CharIter) + if cur.LineIter.GetPrev() == nil { + return + } + + if cur.ScreenHead.LineIter == cur.LineIter { + if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRowsCount { + cur.ScrollUp() + } else { + cur.ScreenHead.Up() + } + } + + index := cur.LineIter.GetValue().Index(cur.CharIter) + cur.CharIter = cur.LineIter.GetPrev().GetValue().GetNodeByIndex(index) + cur.LineIter = cur.LineIter.GetPrev() + cur.Row-- + cur.Col = cur.LineIter.GetValue().Index(cur.CharIter) + // пересчитываем screen left + cur.FixLeftScreen() } func (cur *Cursor) MoveDown() { - if cur.LineIter.GetNext() == nil { - return - } - - if cur.ScreenTail.LineIter == cur.LineIter { - if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRow { - cur.ScrollDown() - } - } - - index := cur.LineIter.GetValue().Index(cur.CharIter) - cur.CharIter = cur.LineIter.GetNext().GetValue().GetNodeByIndex(index) - cur.LineIter = cur.LineIter.GetNext() - cur.Row++ - cur.Col = cur.LineIter.GetValue().Index(cur.CharIter) + if cur.LineIter.GetNext() == nil { + return + } + + if cur.ScreenTail.LineIter == cur.LineIter { + if cur.ScreenTail.RowNumber-cur.ScreenHead.RowNumber+1 >= cur.ScreenRowsCount { + cur.ScrollDown() + } + } + + index := cur.LineIter.GetValue().Index(cur.CharIter) + cur.CharIter = cur.LineIter.GetNext().GetValue().GetNodeByIndex(index) + cur.LineIter = cur.LineIter.GetNext() + cur.Row++ + cur.Col = cur.LineIter.GetValue().Index(cur.CharIter) + // пересчитываем screen left + cur.FixLeftScreen() } func (cur *Cursor) MoveHome() { - cur.CharIter = nil - cur.Col = -1 + cur.CharIter = nil + cur.Col = -1 + // пересчитываем screen left + cur.FixLeftScreen() } func (cur *Cursor) MoveEnd() { - cur.CharIter = cur.LineIter.GetValue().GetTail() - cur.Col = cur.LineIter.GetValue().Length() - 1 + cur.CharIter = cur.LineIter.GetValue().GetTail() + cur.Col = cur.LineIter.GetValue().Length() - 1 + // пересчитываем screen left + cur.FixLeftScreen() } -